@retrivora-ai/rag-engine 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ChromaDBProvider-QNI7UCX4.mjs → ChromaDBProvider-2JZSBOQX.mjs} +1 -1
- package/dist/{MilvusProvider-OO6QGZDZ.mjs → MilvusProvider-CJDBCBVI.mjs} +1 -1
- package/dist/{PineconeProvider-ZRAFNFEC.mjs → PineconeProvider-E6L5Z2FO.mjs} +1 -1
- package/dist/{QdrantProvider-VAED5VA7.mjs → QdrantProvider-JITRNJQN.mjs} +1 -1
- 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-3VKFQXXD.mjs} +1 -1
- package/dist/UniversalVectorProvider-E6L4U4OX.mjs +9 -0
- package/dist/{WeaviateProvider-PSDCUGC7.mjs → WeaviateProvider-MXIPP44J.mjs} +1 -1
- package/dist/{chunk-CWQQHAF6.mjs → chunk-26EMHLIN.mjs} +3 -5
- package/dist/{chunk-VPNRDXIA.mjs → chunk-7BQI4A5J.mjs} +17 -11
- package/dist/{chunk-ZM6TYIDH.mjs → chunk-MFWJZVF3.mjs} +3 -1
- package/dist/{chunk-V75V7BT2.mjs → chunk-TSX6DQXX.mjs} +2 -2
- package/dist/{chunk-HUGLYKD6.mjs → chunk-XZPVJS2B.mjs} +27 -9
- package/dist/chunk-Y6HQZDCJ.mjs +156 -0
- package/dist/{chunk-7YQWGERZ.mjs → chunk-YIYDJQJM.mjs} +736 -189
- package/dist/{chunk-7NXI6ZWX.mjs → chunk-YST6KYBJ.mjs} +8 -5
- 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 +1 -2
- 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/server.d.mts +566 -17
- package/dist/server.d.ts +566 -17
- package/dist/server.js +1233 -642
- package/dist/server.mjs +314 -16
- package/package.json +11 -2
- 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/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 +28 -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
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
mergeDefined
|
|
2
|
+
buildPayload,
|
|
3
|
+
mergeDefined,
|
|
4
|
+
resolvePath
|
|
6
5
|
} from "./chunk-UKDXCXW7.mjs";
|
|
7
6
|
import {
|
|
8
7
|
__spreadProps,
|
|
@@ -13,7 +12,20 @@ import {
|
|
|
13
12
|
import { NextResponse } from "next/server";
|
|
14
13
|
|
|
15
14
|
// src/config/serverConfig.ts
|
|
16
|
-
var VECTOR_DB_PROVIDERS = [
|
|
15
|
+
var VECTOR_DB_PROVIDERS = [
|
|
16
|
+
"pinecone",
|
|
17
|
+
"pgvector",
|
|
18
|
+
"postgresql",
|
|
19
|
+
"mongodb",
|
|
20
|
+
"milvus",
|
|
21
|
+
"qdrant",
|
|
22
|
+
"chromadb",
|
|
23
|
+
"redis",
|
|
24
|
+
"weaviate",
|
|
25
|
+
"rest",
|
|
26
|
+
"universal_rest",
|
|
27
|
+
"custom"
|
|
28
|
+
];
|
|
17
29
|
var LLM_PROVIDERS = ["openai", "anthropic", "ollama", "rest", "universal_rest", "custom"];
|
|
18
30
|
var EMBEDDING_PROVIDERS = ["openai", "ollama", "rest", "universal_rest", "custom"];
|
|
19
31
|
function readString(env, name) {
|
|
@@ -39,7 +51,7 @@ function readEnum(env, name, fallback, allowed) {
|
|
|
39
51
|
throw new Error(`[getRagConfig] ${name} must be one of: ${allowed.join(", ")}`);
|
|
40
52
|
}
|
|
41
53
|
function getRagConfig(env = process.env) {
|
|
42
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K;
|
|
54
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O;
|
|
43
55
|
const projectId = (_b = (_a = readString(env, "RAG_PROJECT_ID")) != null ? _a : readString(env, "NEXT_PUBLIC_PROJECT_ID")) != null ? _b : "__default__";
|
|
44
56
|
const vectorProvider = readEnum(env, "VECTOR_DB_PROVIDER", "pinecone", VECTOR_DB_PROVIDERS);
|
|
45
57
|
const llmProvider = readEnum(env, "LLM_PROVIDER", "openai", LLM_PROVIDERS);
|
|
@@ -48,49 +60,62 @@ function getRagConfig(env = process.env) {
|
|
|
48
60
|
const vectorDbOptions = {};
|
|
49
61
|
if (vectorProvider === "pinecone") {
|
|
50
62
|
vectorDbOptions.apiKey = (_c = readString(env, "PINECONE_API_KEY")) != null ? _c : "";
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
vectorDbOptions.
|
|
63
|
+
} else if (vectorProvider === "pgvector" || vectorProvider === "postgresql") {
|
|
64
|
+
vectorDbOptions.connectionString = (_d = readString(env, "PGVECTOR_CONNECTION_STRING")) != null ? _d : "";
|
|
65
|
+
vectorDbOptions.dimensions = embeddingDimensions;
|
|
66
|
+
} else if (vectorProvider === "mongodb") {
|
|
67
|
+
vectorDbOptions.uri = (_e = readString(env, "MONGODB_URI")) != null ? _e : "";
|
|
68
|
+
vectorDbOptions.database = (_f = readString(env, "MONGODB_DB")) != null ? _f : "";
|
|
69
|
+
vectorDbOptions.collection = (_g = readString(env, "MONGODB_COLLECTION")) != null ? _g : "";
|
|
70
|
+
vectorDbOptions.indexName = (_h = readString(env, "MONGODB_INDEX_NAME")) != null ? _h : "vector_index";
|
|
71
|
+
} else if (vectorProvider === "qdrant") {
|
|
72
|
+
vectorDbOptions.baseUrl = (_i = readString(env, "QDRANT_URL")) != null ? _i : "http://localhost:6333";
|
|
73
|
+
vectorDbOptions.apiKey = readString(env, "QDRANT_API_KEY");
|
|
54
74
|
vectorDbOptions.dimensions = embeddingDimensions;
|
|
75
|
+
} else if (vectorProvider === "milvus") {
|
|
76
|
+
vectorDbOptions.baseUrl = (_j = readString(env, "MILVUS_URL")) != null ? _j : "http://localhost:19530";
|
|
77
|
+
vectorDbOptions.apiKey = readString(env, "MILVUS_API_KEY");
|
|
78
|
+
} else if (vectorProvider === "chromadb") {
|
|
79
|
+
vectorDbOptions.baseUrl = (_k = readString(env, "CHROMADB_URL")) != null ? _k : "http://localhost:8000";
|
|
80
|
+
} else if (vectorProvider === "weaviate") {
|
|
81
|
+
vectorDbOptions.baseUrl = (_l = readString(env, "WEAVIATE_URL")) != null ? _l : "http://localhost:8080";
|
|
82
|
+
vectorDbOptions.apiKey = readString(env, "WEAVIATE_API_KEY");
|
|
83
|
+
} else if (vectorProvider === "redis") {
|
|
84
|
+
vectorDbOptions.baseUrl = (_m = readString(env, "REDIS_URL")) != null ? _m : "";
|
|
85
|
+
vectorDbOptions.apiKey = readString(env, "REDIS_API_KEY");
|
|
55
86
|
} else if (vectorProvider === "rest") {
|
|
56
|
-
vectorDbOptions.baseUrl = (
|
|
87
|
+
vectorDbOptions.baseUrl = (_n = readString(env, "VECTOR_DB_REST_URL")) != null ? _n : "";
|
|
57
88
|
vectorDbOptions.headers = readString(env, "VECTOR_DB_REST_API_KEY") ? { "api-key": readString(env, "VECTOR_DB_REST_API_KEY") } : {};
|
|
58
89
|
} else if (vectorProvider === "universal_rest") {
|
|
59
|
-
vectorDbOptions.baseUrl = (
|
|
90
|
+
vectorDbOptions.baseUrl = (_p = (_o = readString(env, "VECTOR_BASE_URL")) != null ? _o : readString(env, "VECTOR_DB_REST_URL")) != null ? _p : "";
|
|
60
91
|
vectorDbOptions.profile = readString(env, "VECTOR_UNIVERSAL_PROFILE");
|
|
61
92
|
vectorDbOptions.headers = readString(env, "VECTOR_DB_REST_API_KEY") ? { Authorization: `Bearer ${readString(env, "VECTOR_DB_REST_API_KEY")}` } : {};
|
|
62
|
-
} else if (vectorProvider === "mongodb") {
|
|
63
|
-
vectorDbOptions.uri = (_i = readString(env, "MONGODB_URI")) != null ? _i : "";
|
|
64
|
-
vectorDbOptions.database = (_j = readString(env, "MONGODB_DB")) != null ? _j : "";
|
|
65
|
-
vectorDbOptions.collection = (_k = readString(env, "MONGODB_COLLECTION")) != null ? _k : "";
|
|
66
|
-
vectorDbOptions.indexName = (_l = readString(env, "MONGODB_INDEX_NAME")) != null ? _l : "vector_index";
|
|
67
|
-
} else if (vectorProvider === "qdrant") {
|
|
68
|
-
vectorDbOptions.baseUrl = (_m = readString(env, "QDRANT_URL")) != null ? _m : "http://localhost:6333";
|
|
69
|
-
vectorDbOptions.apiKey = readString(env, "QDRANT_API_KEY");
|
|
70
|
-
vectorDbOptions.dimensions = embeddingDimensions;
|
|
71
93
|
}
|
|
72
94
|
const llmApiKeyByProvider = {
|
|
73
95
|
openai: readString(env, "OPENAI_API_KEY"),
|
|
74
96
|
anthropic: readString(env, "ANTHROPIC_API_KEY"),
|
|
75
97
|
ollama: void 0,
|
|
76
|
-
universal_rest: readString(env, "LLM_API_KEY")
|
|
98
|
+
universal_rest: readString(env, "LLM_API_KEY"),
|
|
99
|
+
rest: readString(env, "LLM_API_KEY"),
|
|
100
|
+
custom: readString(env, "LLM_API_KEY")
|
|
77
101
|
};
|
|
78
102
|
const embeddingApiKeyByProvider = {
|
|
79
103
|
openai: readString(env, "OPENAI_API_KEY"),
|
|
80
104
|
ollama: void 0,
|
|
81
|
-
|
|
105
|
+
universal_rest: (_q = readString(env, "EMBEDDING_API_KEY")) != null ? _q : readString(env, "OPENAI_API_KEY"),
|
|
106
|
+
custom: (_r = readString(env, "EMBEDDING_API_KEY")) != null ? _r : readString(env, "OPENAI_API_KEY")
|
|
82
107
|
};
|
|
83
108
|
return {
|
|
84
109
|
projectId,
|
|
85
110
|
vectorDb: {
|
|
86
111
|
provider: vectorProvider,
|
|
87
|
-
indexName: (
|
|
112
|
+
indexName: (_s = readString(env, "VECTOR_DB_INDEX")) != null ? _s : "rag-index",
|
|
88
113
|
options: vectorDbOptions
|
|
89
114
|
},
|
|
90
115
|
llm: {
|
|
91
116
|
provider: llmProvider,
|
|
92
|
-
model: (
|
|
93
|
-
apiKey: (
|
|
117
|
+
model: (_t = readString(env, "LLM_MODEL")) != null ? _t : "gpt-4o",
|
|
118
|
+
apiKey: (_u = llmApiKeyByProvider[llmProvider]) != null ? _u : "",
|
|
94
119
|
baseUrl: readString(env, "LLM_BASE_URL"),
|
|
95
120
|
systemPrompt: readString(env, "LLM_SYSTEM_PROMPT"),
|
|
96
121
|
maxTokens: readNumber(env, "LLM_MAX_TOKENS", 1024),
|
|
@@ -101,7 +126,7 @@ function getRagConfig(env = process.env) {
|
|
|
101
126
|
},
|
|
102
127
|
embedding: {
|
|
103
128
|
provider: embeddingProvider,
|
|
104
|
-
model: (
|
|
129
|
+
model: (_v = readString(env, "EMBEDDING_MODEL")) != null ? _v : "text-embedding-3-small",
|
|
105
130
|
apiKey: embeddingApiKeyByProvider[embeddingProvider],
|
|
106
131
|
baseUrl: readString(env, "EMBEDDING_BASE_URL"),
|
|
107
132
|
dimensions: embeddingDimensions,
|
|
@@ -110,16 +135,16 @@ function getRagConfig(env = process.env) {
|
|
|
110
135
|
}
|
|
111
136
|
},
|
|
112
137
|
ui: {
|
|
113
|
-
title: (
|
|
114
|
-
subtitle: (
|
|
115
|
-
primaryColor: (
|
|
116
|
-
accentColor: (
|
|
117
|
-
logoUrl: (
|
|
118
|
-
placeholder: (
|
|
119
|
-
showSources: ((
|
|
120
|
-
welcomeMessage: (
|
|
121
|
-
visualStyle: (
|
|
122
|
-
borderRadius: (
|
|
138
|
+
title: (_x = (_w = readString(env, "NEXT_PUBLIC_UI_TITLE")) != null ? _w : readString(env, "UI_TITLE")) != null ? _x : "AI Assistant",
|
|
139
|
+
subtitle: (_z = (_y = readString(env, "NEXT_PUBLIC_UI_SUBTITLE")) != null ? _y : readString(env, "UI_SUBTITLE")) != null ? _z : "Powered by RAG",
|
|
140
|
+
primaryColor: (_B = (_A = readString(env, "NEXT_PUBLIC_PRIMARY_COLOR")) != null ? _A : readString(env, "UI_PRIMARY_COLOR")) != null ? _B : "#10b981",
|
|
141
|
+
accentColor: (_D = (_C = readString(env, "NEXT_PUBLIC_ACCENT_COLOR")) != null ? _C : readString(env, "UI_ACCENT_COLOR")) != null ? _D : "#3b82f6",
|
|
142
|
+
logoUrl: (_E = readString(env, "NEXT_PUBLIC_LOGO_URL")) != null ? _E : readString(env, "UI_LOGO_URL"),
|
|
143
|
+
placeholder: (_G = (_F = readString(env, "NEXT_PUBLIC_PLACEHOLDER")) != null ? _F : readString(env, "UI_PLACEHOLDER")) != null ? _G : "Ask me anything\u2026",
|
|
144
|
+
showSources: ((_I = (_H = readString(env, "NEXT_PUBLIC_SHOW_SOURCES")) != null ? _H : readString(env, "UI_SHOW_SOURCES")) != null ? _I : "true") !== "false",
|
|
145
|
+
welcomeMessage: (_K = (_J = readString(env, "NEXT_PUBLIC_WELCOME_MESSAGE")) != null ? _J : readString(env, "UI_WELCOME_MESSAGE")) != null ? _K : "Hello! I'm your AI assistant. Ask me anything about your documents.",
|
|
146
|
+
visualStyle: (_M = (_L = readString(env, "NEXT_PUBLIC_UI_VISUAL_STYLE")) != null ? _L : readString(env, "UI_VISUAL_STYLE")) != null ? _M : "glass",
|
|
147
|
+
borderRadius: (_O = (_N = readString(env, "NEXT_PUBLIC_UI_BORDER_RADIUS")) != null ? _N : readString(env, "UI_BORDER_RADIUS")) != null ? _O : "xl"
|
|
123
148
|
},
|
|
124
149
|
rag: {
|
|
125
150
|
topK: readNumber(env, "RAG_TOP_K", 5),
|
|
@@ -195,7 +220,7 @@ var ConfigValidator = class {
|
|
|
195
220
|
errors.push({
|
|
196
221
|
field: "embedding",
|
|
197
222
|
message: "Embedding config is required when using Anthropic LLM",
|
|
198
|
-
suggestion: 'Provide embedding config with provider (e.g., "openai")',
|
|
223
|
+
suggestion: 'Provide embedding config with provider (e.g., "openai" or "ollama")',
|
|
199
224
|
severity: "error"
|
|
200
225
|
});
|
|
201
226
|
}
|
|
@@ -257,6 +282,10 @@ var ConfigValidator = class {
|
|
|
257
282
|
}
|
|
258
283
|
return errors;
|
|
259
284
|
}
|
|
285
|
+
/**
|
|
286
|
+
* Pinecone — only needs apiKey (environment was removed in SDK v7+).
|
|
287
|
+
* Options key: { apiKey: string }
|
|
288
|
+
*/
|
|
260
289
|
static validatePineconeConfig(config) {
|
|
261
290
|
const errors = [];
|
|
262
291
|
const opts = config.options;
|
|
@@ -268,16 +297,12 @@ var ConfigValidator = class {
|
|
|
268
297
|
severity: "error"
|
|
269
298
|
});
|
|
270
299
|
}
|
|
271
|
-
if (!opts.environment) {
|
|
272
|
-
errors.push({
|
|
273
|
-
field: "vectorDb.options.environment",
|
|
274
|
-
message: "Pinecone environment is required",
|
|
275
|
-
suggestion: 'Set PINECONE_ENVIRONMENT environment variable (e.g., "gcp-starter")',
|
|
276
|
-
severity: "error"
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
300
|
return errors;
|
|
280
301
|
}
|
|
302
|
+
/**
|
|
303
|
+
* PostgreSQL / pgvector — needs a connection string.
|
|
304
|
+
* Options key: { connectionString: string }
|
|
305
|
+
*/
|
|
281
306
|
static validatePostgresConfig(config) {
|
|
282
307
|
const errors = [];
|
|
283
308
|
const opts = config.options;
|
|
@@ -291,6 +316,10 @@ var ConfigValidator = class {
|
|
|
291
316
|
}
|
|
292
317
|
return errors;
|
|
293
318
|
}
|
|
319
|
+
/**
|
|
320
|
+
* MongoDB — needs uri, database, and collection.
|
|
321
|
+
* Options keys: { uri: string, database: string, collection: string }
|
|
322
|
+
*/
|
|
294
323
|
static validateMongoDBConfig(config) {
|
|
295
324
|
const errors = [];
|
|
296
325
|
const opts = config.options;
|
|
@@ -320,27 +349,30 @@ var ConfigValidator = class {
|
|
|
320
349
|
}
|
|
321
350
|
return errors;
|
|
322
351
|
}
|
|
352
|
+
/**
|
|
353
|
+
* Milvus — accepts baseUrl OR uri (preferred) OR host+port.
|
|
354
|
+
* MilvusProvider reads opts.baseUrl ?? opts.uri.
|
|
355
|
+
* Options keys: { baseUrl?: string, uri?: string, host?: string, port?: number }
|
|
356
|
+
*/
|
|
323
357
|
static validateMilvusConfig(config) {
|
|
324
358
|
const errors = [];
|
|
325
359
|
const opts = config.options;
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
message: "Milvus host is required",
|
|
330
|
-
suggestion: "Set MILVUS_HOST environment variable",
|
|
331
|
-
severity: "error"
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
if (!opts.port) {
|
|
360
|
+
const hasBaseUrl = Boolean(opts.baseUrl || opts.uri);
|
|
361
|
+
const hasHostPort = Boolean(opts.host && opts.port);
|
|
362
|
+
if (!hasBaseUrl && !hasHostPort) {
|
|
335
363
|
errors.push({
|
|
336
|
-
field: "vectorDb.options.
|
|
337
|
-
message:
|
|
338
|
-
suggestion: "Set
|
|
364
|
+
field: "vectorDb.options.baseUrl",
|
|
365
|
+
message: 'Milvus connection is required: provide baseUrl (e.g., "http://localhost:19530") or host + port',
|
|
366
|
+
suggestion: "Set MILVUS_URL environment variable",
|
|
339
367
|
severity: "error"
|
|
340
368
|
});
|
|
341
369
|
}
|
|
342
370
|
return errors;
|
|
343
371
|
}
|
|
372
|
+
/**
|
|
373
|
+
* Qdrant — needs baseUrl (or url alias).
|
|
374
|
+
* Options keys: { baseUrl: string, apiKey?: string }
|
|
375
|
+
*/
|
|
344
376
|
static validateQdrantConfig(config) {
|
|
345
377
|
const errors = [];
|
|
346
378
|
const opts = config.options;
|
|
@@ -354,38 +386,55 @@ var ConfigValidator = class {
|
|
|
354
386
|
}
|
|
355
387
|
return errors;
|
|
356
388
|
}
|
|
389
|
+
/**
|
|
390
|
+
* ChromaDB — accepts baseUrl OR host.
|
|
391
|
+
* ChromaDBProvider reads opts.baseUrl.
|
|
392
|
+
* Options keys: { baseUrl?: string, host?: string, port?: number }
|
|
393
|
+
*/
|
|
357
394
|
static validateChromaDBConfig(config) {
|
|
358
395
|
const errors = [];
|
|
359
396
|
const opts = config.options;
|
|
360
|
-
if (!opts.
|
|
397
|
+
if (!opts.baseUrl && !opts.host) {
|
|
361
398
|
errors.push({
|
|
362
|
-
field: "vectorDb.options.
|
|
363
|
-
message:
|
|
364
|
-
suggestion: "Set
|
|
399
|
+
field: "vectorDb.options.baseUrl",
|
|
400
|
+
message: 'ChromaDB connection is required: provide baseUrl (e.g., "http://localhost:8000") or host',
|
|
401
|
+
suggestion: "Set CHROMADB_URL environment variable",
|
|
365
402
|
severity: "error"
|
|
366
403
|
});
|
|
367
404
|
}
|
|
368
405
|
return errors;
|
|
369
406
|
}
|
|
407
|
+
/**
|
|
408
|
+
* Redis — accepts baseUrl OR url OR host+port.
|
|
409
|
+
* RedisProvider reads opts.baseUrl.
|
|
410
|
+
* Options keys: { baseUrl?: string, url?: string, host?: string, port?: number, apiKey?: string }
|
|
411
|
+
*/
|
|
370
412
|
static validateRedisConfig(config) {
|
|
371
413
|
const errors = [];
|
|
372
414
|
const opts = config.options;
|
|
373
|
-
|
|
415
|
+
const hasUrl = Boolean(opts.baseUrl || opts.url);
|
|
416
|
+
const hasHostPort = Boolean(opts.host && opts.port);
|
|
417
|
+
if (!hasUrl && !hasHostPort) {
|
|
374
418
|
errors.push({
|
|
375
|
-
field: "vectorDb.options.
|
|
376
|
-
message: "Redis connection
|
|
377
|
-
suggestion: "Set REDIS_URL
|
|
419
|
+
field: "vectorDb.options.baseUrl",
|
|
420
|
+
message: "Redis connection is required: provide baseUrl/url or host + port",
|
|
421
|
+
suggestion: "Set REDIS_URL environment variable",
|
|
378
422
|
severity: "error"
|
|
379
423
|
});
|
|
380
424
|
}
|
|
381
425
|
return errors;
|
|
382
426
|
}
|
|
427
|
+
/**
|
|
428
|
+
* Weaviate — accepts baseUrl OR url.
|
|
429
|
+
* WeaviateProvider reads opts.baseUrl.
|
|
430
|
+
* Options keys: { baseUrl?: string, url?: string, apiKey?: string }
|
|
431
|
+
*/
|
|
383
432
|
static validateWeaviateConfig(config) {
|
|
384
433
|
const errors = [];
|
|
385
434
|
const opts = config.options;
|
|
386
|
-
if (!opts.url) {
|
|
435
|
+
if (!opts.baseUrl && !opts.url) {
|
|
387
436
|
errors.push({
|
|
388
|
-
field: "vectorDb.options.
|
|
437
|
+
field: "vectorDb.options.baseUrl",
|
|
389
438
|
message: "Weaviate instance URL is required",
|
|
390
439
|
suggestion: "Set WEAVIATE_URL environment variable",
|
|
391
440
|
severity: "error"
|
|
@@ -393,6 +442,10 @@ var ConfigValidator = class {
|
|
|
393
442
|
}
|
|
394
443
|
return errors;
|
|
395
444
|
}
|
|
445
|
+
/**
|
|
446
|
+
* Universal REST / custom REST adapter.
|
|
447
|
+
* Options key: { baseUrl: string }
|
|
448
|
+
*/
|
|
396
449
|
static validateRestConfig(config) {
|
|
397
450
|
const errors = [];
|
|
398
451
|
const opts = config.options;
|
|
@@ -500,7 +553,7 @@ var ConfigValidator = class {
|
|
|
500
553
|
errors.push({
|
|
501
554
|
field: "embedding.model",
|
|
502
555
|
message: "Embedding model name is required",
|
|
503
|
-
suggestion: 'e.g., "text-embedding-3-small" for OpenAI',
|
|
556
|
+
suggestion: 'e.g., "text-embedding-3-small" for OpenAI, "nomic-embed-text" for Ollama',
|
|
504
557
|
severity: "error"
|
|
505
558
|
});
|
|
506
559
|
}
|
|
@@ -515,7 +568,7 @@ var ConfigValidator = class {
|
|
|
515
568
|
if (config.provider === "ollama" && !config.baseUrl) {
|
|
516
569
|
errors.push({
|
|
517
570
|
field: "embedding.baseUrl",
|
|
518
|
-
message: "Ollama base URL is required",
|
|
571
|
+
message: "Ollama base URL is required for embedding",
|
|
519
572
|
suggestion: "Set EMBEDDING_BASE_URL environment variable",
|
|
520
573
|
severity: "error"
|
|
521
574
|
});
|
|
@@ -692,16 +745,432 @@ var DocumentChunker = class {
|
|
|
692
745
|
}
|
|
693
746
|
};
|
|
694
747
|
|
|
748
|
+
// src/llm/providers/OpenAIProvider.ts
|
|
749
|
+
import OpenAI from "openai";
|
|
750
|
+
var OpenAIProvider = class {
|
|
751
|
+
constructor(llmConfig, embeddingConfig) {
|
|
752
|
+
if (!llmConfig.apiKey) throw new Error("[OpenAIProvider] llmConfig.apiKey is required");
|
|
753
|
+
this.client = new OpenAI({ apiKey: llmConfig.apiKey });
|
|
754
|
+
this.llmConfig = llmConfig;
|
|
755
|
+
this.embeddingConfig = embeddingConfig;
|
|
756
|
+
}
|
|
757
|
+
async chat(messages, context, options) {
|
|
758
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
759
|
+
const systemContent = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Answer questions based on the provided context.
|
|
760
|
+
|
|
761
|
+
Context:
|
|
762
|
+
${context}`;
|
|
763
|
+
const systemMessage = {
|
|
764
|
+
role: "system",
|
|
765
|
+
content: systemContent.includes("{{context}}") ? systemContent.replace("{{context}}", context) : `${systemContent}
|
|
766
|
+
|
|
767
|
+
Context:
|
|
768
|
+
${context}`
|
|
769
|
+
};
|
|
770
|
+
const formattedMessages = [
|
|
771
|
+
systemMessage,
|
|
772
|
+
...messages.map((m) => ({
|
|
773
|
+
role: m.role,
|
|
774
|
+
content: m.content
|
|
775
|
+
}))
|
|
776
|
+
];
|
|
777
|
+
const completion = await this.client.chat.completions.create({
|
|
778
|
+
model: this.llmConfig.model,
|
|
779
|
+
messages: formattedMessages,
|
|
780
|
+
max_tokens: (_c = (_b = options == null ? void 0 : options.maxTokens) != null ? _b : this.llmConfig.maxTokens) != null ? _c : 1024,
|
|
781
|
+
temperature: (_e = (_d = options == null ? void 0 : options.temperature) != null ? _d : this.llmConfig.temperature) != null ? _e : 0.7,
|
|
782
|
+
stop: options == null ? void 0 : options.stop
|
|
783
|
+
});
|
|
784
|
+
return (_h = (_g = (_f = completion.choices[0]) == null ? void 0 : _f.message) == null ? void 0 : _g.content) != null ? _h : "";
|
|
785
|
+
}
|
|
786
|
+
async embed(text, options) {
|
|
787
|
+
const results = await this.batchEmbed([text], options);
|
|
788
|
+
return results[0];
|
|
789
|
+
}
|
|
790
|
+
async batchEmbed(texts, options) {
|
|
791
|
+
var _a, _b, _c, _d, _e;
|
|
792
|
+
const model = (_c = (_b = options == null ? void 0 : options.model) != null ? _b : (_a = this.embeddingConfig) == null ? void 0 : _a.model) != null ? _c : "text-embedding-3-small";
|
|
793
|
+
const apiKey = (_e = (_d = this.embeddingConfig) == null ? void 0 : _d.apiKey) != null ? _e : this.llmConfig.apiKey;
|
|
794
|
+
const client = apiKey !== this.llmConfig.apiKey ? new OpenAI({ apiKey }) : this.client;
|
|
795
|
+
const response = await client.embeddings.create({ model, input: texts });
|
|
796
|
+
return response.data.map((d) => d.embedding);
|
|
797
|
+
}
|
|
798
|
+
async ping() {
|
|
799
|
+
try {
|
|
800
|
+
await this.client.models.list();
|
|
801
|
+
return true;
|
|
802
|
+
} catch (err) {
|
|
803
|
+
console.error("[OpenAIProvider] Ping failed:", err);
|
|
804
|
+
return false;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
|
|
809
|
+
// src/llm/providers/AnthropicProvider.ts
|
|
810
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
811
|
+
var AnthropicProvider = class {
|
|
812
|
+
constructor(llmConfig, embeddingConfig) {
|
|
813
|
+
if (!llmConfig.apiKey) throw new Error("[AnthropicProvider] llmConfig.apiKey is required");
|
|
814
|
+
this.client = new Anthropic({ apiKey: llmConfig.apiKey });
|
|
815
|
+
this.llmConfig = llmConfig;
|
|
816
|
+
this.embeddingConfig = embeddingConfig;
|
|
817
|
+
}
|
|
818
|
+
async chat(messages, context, options) {
|
|
819
|
+
var _a, _b, _c;
|
|
820
|
+
const systemPrompt = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Use the context below to answer the user's question accurately.
|
|
821
|
+
|
|
822
|
+
Context:
|
|
823
|
+
${context}`;
|
|
824
|
+
const system = systemPrompt.includes("{{context}}") ? systemPrompt.replace("{{context}}", context) : `${systemPrompt}
|
|
825
|
+
|
|
826
|
+
Context:
|
|
827
|
+
${context}`;
|
|
828
|
+
const anthropicMessages = messages.map((m) => ({
|
|
829
|
+
role: m.role === "assistant" ? "assistant" : "user",
|
|
830
|
+
content: m.content
|
|
831
|
+
}));
|
|
832
|
+
const response = await this.client.messages.create({
|
|
833
|
+
model: this.llmConfig.model,
|
|
834
|
+
max_tokens: (_c = (_b = options == null ? void 0 : options.maxTokens) != null ? _b : this.llmConfig.maxTokens) != null ? _c : 1024,
|
|
835
|
+
system,
|
|
836
|
+
messages: anthropicMessages
|
|
837
|
+
});
|
|
838
|
+
const block = response.content[0];
|
|
839
|
+
return block.type === "text" ? block.text : "";
|
|
840
|
+
}
|
|
841
|
+
/**
|
|
842
|
+
* Anthropic does not offer an embedding API.
|
|
843
|
+
* This method throws with a clear error so developers know to configure
|
|
844
|
+
* a separate embedding provider (OpenAI or Ollama).
|
|
845
|
+
*/
|
|
846
|
+
async embed(text, options) {
|
|
847
|
+
void text;
|
|
848
|
+
void options;
|
|
849
|
+
throw new Error(
|
|
850
|
+
'[AnthropicProvider] Anthropic does not provide an embedding API. Set embedding.provider to "openai" or "ollama" in your RagConfig.'
|
|
851
|
+
);
|
|
852
|
+
}
|
|
853
|
+
async batchEmbed(texts, options) {
|
|
854
|
+
void texts;
|
|
855
|
+
void options;
|
|
856
|
+
throw new Error(
|
|
857
|
+
"[AnthropicProvider] Anthropic does not provide an embedding API."
|
|
858
|
+
);
|
|
859
|
+
}
|
|
860
|
+
async ping() {
|
|
861
|
+
try {
|
|
862
|
+
await this.client.models.list();
|
|
863
|
+
return true;
|
|
864
|
+
} catch (err) {
|
|
865
|
+
console.error("[AnthropicProvider] Ping failed:", err);
|
|
866
|
+
return false;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
};
|
|
870
|
+
|
|
871
|
+
// src/llm/providers/OllamaProvider.ts
|
|
872
|
+
import axios from "axios";
|
|
873
|
+
var OllamaProvider = class {
|
|
874
|
+
constructor(llmConfig, embeddingConfig) {
|
|
875
|
+
var _a;
|
|
876
|
+
const baseURL = (_a = llmConfig.baseUrl) != null ? _a : "http://localhost:11434";
|
|
877
|
+
this.http = axios.create({ baseURL, timeout: 12e4 });
|
|
878
|
+
this.llmConfig = llmConfig;
|
|
879
|
+
this.embeddingConfig = embeddingConfig;
|
|
880
|
+
}
|
|
881
|
+
async chat(messages, context, options) {
|
|
882
|
+
var _a, _b, _c, _d, _e;
|
|
883
|
+
const systemPrompt = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Use the provided context to answer the user's question.
|
|
884
|
+
|
|
885
|
+
Context:
|
|
886
|
+
${context}`;
|
|
887
|
+
const system = systemPrompt.includes("{{context}}") ? systemPrompt.replace("{{context}}", context) : `${systemPrompt}
|
|
888
|
+
|
|
889
|
+
Context:
|
|
890
|
+
${context}`;
|
|
891
|
+
const { data } = await this.http.post("/api/chat", {
|
|
892
|
+
model: this.llmConfig.model,
|
|
893
|
+
stream: false,
|
|
894
|
+
options: {
|
|
895
|
+
temperature: (_c = (_b = options == null ? void 0 : options.temperature) != null ? _b : this.llmConfig.temperature) != null ? _c : 0.7,
|
|
896
|
+
num_predict: (_e = (_d = options == null ? void 0 : options.maxTokens) != null ? _d : this.llmConfig.maxTokens) != null ? _e : 1024
|
|
897
|
+
},
|
|
898
|
+
messages: [
|
|
899
|
+
{ role: "system", content: system },
|
|
900
|
+
...messages.map((m) => ({ role: m.role, content: m.content }))
|
|
901
|
+
]
|
|
902
|
+
});
|
|
903
|
+
return data.message.content;
|
|
904
|
+
}
|
|
905
|
+
async embed(text, options) {
|
|
906
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
907
|
+
const model = (_c = (_b = options == null ? void 0 : options.model) != null ? _b : (_a = this.embeddingConfig) == null ? void 0 : _a.model) != null ? _c : "nomic-embed-text";
|
|
908
|
+
const baseURL = (_f = (_e = (_d = this.embeddingConfig) == null ? void 0 : _d.baseUrl) != null ? _e : this.llmConfig.baseUrl) != null ? _f : "http://localhost:11434";
|
|
909
|
+
const client = baseURL !== ((_g = this.llmConfig.baseUrl) != null ? _g : "http://localhost:11434") ? axios.create({ baseURL, timeout: 6e4 }) : this.http;
|
|
910
|
+
const { data } = await client.post("/api/embeddings", {
|
|
911
|
+
model,
|
|
912
|
+
prompt: text
|
|
913
|
+
});
|
|
914
|
+
return data.embedding;
|
|
915
|
+
}
|
|
916
|
+
async batchEmbed(texts, options) {
|
|
917
|
+
const vectors = [];
|
|
918
|
+
for (const text of texts) {
|
|
919
|
+
vectors.push(await this.embed(text, options));
|
|
920
|
+
}
|
|
921
|
+
return vectors;
|
|
922
|
+
}
|
|
923
|
+
async ping() {
|
|
924
|
+
try {
|
|
925
|
+
await this.http.get("/api/tags");
|
|
926
|
+
return true;
|
|
927
|
+
} catch (err) {
|
|
928
|
+
console.error("[OllamaProvider] Ping failed:", err);
|
|
929
|
+
return false;
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
};
|
|
933
|
+
|
|
934
|
+
// src/llm/providers/UniversalLLMAdapter.ts
|
|
935
|
+
import axios2 from "axios";
|
|
936
|
+
|
|
937
|
+
// src/config/UniversalProfiles.ts
|
|
938
|
+
var OPENAI_BASE = {
|
|
939
|
+
chatPath: "/chat/completions",
|
|
940
|
+
embedPath: "/embeddings",
|
|
941
|
+
responseExtractPath: "choices[0].message.content",
|
|
942
|
+
embedExtractPath: "data[0].embedding",
|
|
943
|
+
chatPayloadTemplate: '{"model":"{{model}}","messages":{{messages}},"max_tokens":{{maxTokens}},"temperature":{{temperature}}}'
|
|
944
|
+
};
|
|
945
|
+
var LLM_PROFILES = {
|
|
946
|
+
"openai-compatible": OPENAI_BASE,
|
|
947
|
+
"litellm": __spreadProps(__spreadValues({}, OPENAI_BASE), {
|
|
948
|
+
chatPayloadTemplate: '{"model":"{{model}}","messages":{{messages}},"max_tokens":{{maxTokens}},"temperature":{{temperature}},"stream":false}'
|
|
949
|
+
}),
|
|
950
|
+
"anthropic-claude": {
|
|
951
|
+
chatPath: "/v1/messages",
|
|
952
|
+
responseExtractPath: "content[0].text",
|
|
953
|
+
headers: { "anthropic-version": "2023-06-01" },
|
|
954
|
+
chatPayloadTemplate: '{"model":"{{model}}","messages":{{messages}},"max_tokens":{{maxTokens}}}'
|
|
955
|
+
},
|
|
956
|
+
"google-gemini": {
|
|
957
|
+
chatPath: "/v1beta/models/{{model}}:generateContent",
|
|
958
|
+
responseExtractPath: "candidates[0].content.parts[0].text",
|
|
959
|
+
chatPayloadTemplate: '{"contents":[{"parts":[{"text":"{{messages}}"}]}]}'
|
|
960
|
+
},
|
|
961
|
+
"github-copilot": __spreadProps(__spreadValues({}, OPENAI_BASE), {
|
|
962
|
+
chatPayloadTemplate: '{"model":"{{model}}","messages":{{messages}},"temperature":{{temperature}}}'
|
|
963
|
+
}),
|
|
964
|
+
"ollama-standard": {
|
|
965
|
+
chatPath: "/api/chat",
|
|
966
|
+
embedPath: "/api/embeddings",
|
|
967
|
+
responseExtractPath: "message.content",
|
|
968
|
+
embedExtractPath: "embedding",
|
|
969
|
+
chatPayloadTemplate: '{"model":"{{model}}","messages":{{messages}},"stream":false}',
|
|
970
|
+
embedPayloadTemplate: '{"model":"{{model}}","prompt":"{{input}}"}'
|
|
971
|
+
}
|
|
972
|
+
};
|
|
973
|
+
var VECTOR_PROFILES = {
|
|
974
|
+
"pinecone-rest": {
|
|
975
|
+
queryPath: "/query",
|
|
976
|
+
upsertPath: "/vectors/upsert",
|
|
977
|
+
responseExtractPath: "matches",
|
|
978
|
+
idPath: "id",
|
|
979
|
+
scorePath: "score",
|
|
980
|
+
contentPath: "metadata.content",
|
|
981
|
+
metadataPath: "metadata",
|
|
982
|
+
queryPayloadTemplate: '{"vector":{{vector}},"topK":{{topK}},"includeMetadata":true,"namespace":"{{namespace}}"}'
|
|
983
|
+
},
|
|
984
|
+
"mongodb-atlas": {
|
|
985
|
+
queryPath: "/action/aggregate",
|
|
986
|
+
responseExtractPath: "documents",
|
|
987
|
+
idPath: "_id",
|
|
988
|
+
scorePath: "score",
|
|
989
|
+
contentPath: "content",
|
|
990
|
+
metadataPath: "metadata",
|
|
991
|
+
queryPayloadTemplate: '{"collection":"{{index}}","database":"ai_db","dataSource":"Cluster0","pipeline":[{"$vectorSearch":{"index":"vector_index","path":"embedding","queryVector":{{vector}},"numCandidates":100,"limit":{{topK}}}},{"$project":{"_id":1,"content":1,"metadata":1,"score":{"$meta":"vectorSearchScore"}}}]}'
|
|
992
|
+
},
|
|
993
|
+
"chromadb": {
|
|
994
|
+
queryPath: "/api/v1/collections/{{index}}/query",
|
|
995
|
+
upsertPath: "/api/v1/collections/{{index}}/add",
|
|
996
|
+
responseExtractPath: "matches",
|
|
997
|
+
idPath: "id",
|
|
998
|
+
scorePath: "distance",
|
|
999
|
+
contentPath: "document",
|
|
1000
|
+
metadataPath: "metadata"
|
|
1001
|
+
},
|
|
1002
|
+
"qdrant": {
|
|
1003
|
+
queryPath: "/collections/{{index}}/points/search",
|
|
1004
|
+
upsertPath: "/collections/{{index}}/points",
|
|
1005
|
+
responseExtractPath: "result",
|
|
1006
|
+
idPath: "id",
|
|
1007
|
+
scorePath: "score",
|
|
1008
|
+
contentPath: "payload.content",
|
|
1009
|
+
metadataPath: "payload"
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
1012
|
+
|
|
1013
|
+
// src/llm/providers/UniversalLLMAdapter.ts
|
|
1014
|
+
var UniversalLLMAdapter = class {
|
|
1015
|
+
constructor(config) {
|
|
1016
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
1017
|
+
this.model = config.model;
|
|
1018
|
+
const llmConfig = config;
|
|
1019
|
+
const options = (_a = llmConfig.options) != null ? _a : {};
|
|
1020
|
+
let profile = {};
|
|
1021
|
+
if (typeof options.profile === "string") {
|
|
1022
|
+
profile = LLM_PROFILES[options.profile] || {};
|
|
1023
|
+
} else if (typeof options.profile === "object" && options.profile !== null) {
|
|
1024
|
+
profile = options.profile;
|
|
1025
|
+
}
|
|
1026
|
+
this.opts = __spreadValues(__spreadValues({}, profile), (_b = llmConfig.options) != null ? _b : {});
|
|
1027
|
+
this.systemPrompt = (_c = llmConfig.systemPrompt) != null ? _c : "You are a helpful AI assistant. Use the provided context to answer the user.";
|
|
1028
|
+
this.maxTokens = (_d = llmConfig.maxTokens) != null ? _d : 1024;
|
|
1029
|
+
this.temperature = (_e = llmConfig.temperature) != null ? _e : 0;
|
|
1030
|
+
const baseUrl = (_f = llmConfig.baseUrl) != null ? _f : this.opts.baseUrl;
|
|
1031
|
+
if (!baseUrl) {
|
|
1032
|
+
throw new Error("[UniversalLLMAdapter] baseUrl is required in config or config.options");
|
|
1033
|
+
}
|
|
1034
|
+
this.http = axios2.create({
|
|
1035
|
+
baseURL: baseUrl,
|
|
1036
|
+
headers: __spreadValues(__spreadValues({
|
|
1037
|
+
"Content-Type": "application/json"
|
|
1038
|
+
}, config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}), this.opts.headers || {}),
|
|
1039
|
+
timeout: (_g = this.opts.timeout) != null ? _g : 6e4
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
async chat(messages, context) {
|
|
1043
|
+
var _a, _b;
|
|
1044
|
+
const path = (_a = this.opts.chatPath) != null ? _a : "/chat/completions";
|
|
1045
|
+
const formattedMessages = [
|
|
1046
|
+
{ role: "system", content: `${this.systemPrompt}
|
|
1047
|
+
|
|
1048
|
+
Context:
|
|
1049
|
+
${context != null ? context : "None"}` },
|
|
1050
|
+
...messages
|
|
1051
|
+
];
|
|
1052
|
+
let payload;
|
|
1053
|
+
if (this.opts.chatPayloadTemplate) {
|
|
1054
|
+
payload = buildPayload(this.opts.chatPayloadTemplate, {
|
|
1055
|
+
model: this.model,
|
|
1056
|
+
messages: formattedMessages,
|
|
1057
|
+
maxTokens: this.maxTokens,
|
|
1058
|
+
temperature: this.temperature
|
|
1059
|
+
});
|
|
1060
|
+
} else {
|
|
1061
|
+
payload = {
|
|
1062
|
+
model: this.model,
|
|
1063
|
+
messages: formattedMessages,
|
|
1064
|
+
max_tokens: this.maxTokens,
|
|
1065
|
+
temperature: this.temperature
|
|
1066
|
+
};
|
|
1067
|
+
}
|
|
1068
|
+
const { data } = await this.http.post(path, payload);
|
|
1069
|
+
const extractPath = (_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content";
|
|
1070
|
+
const result = resolvePath(data, extractPath);
|
|
1071
|
+
if (result === void 0) {
|
|
1072
|
+
throw new Error(`[UniversalLLMAdapter] Could not extract text from path '${extractPath}' in response.`);
|
|
1073
|
+
}
|
|
1074
|
+
return String(result);
|
|
1075
|
+
}
|
|
1076
|
+
async embed(text) {
|
|
1077
|
+
var _a, _b;
|
|
1078
|
+
const path = (_a = this.opts.embedPath) != null ? _a : "/embeddings";
|
|
1079
|
+
let payload;
|
|
1080
|
+
if (this.opts.embedPayloadTemplate) {
|
|
1081
|
+
payload = buildPayload(this.opts.embedPayloadTemplate, {
|
|
1082
|
+
model: this.model,
|
|
1083
|
+
input: text
|
|
1084
|
+
});
|
|
1085
|
+
} else {
|
|
1086
|
+
payload = {
|
|
1087
|
+
model: this.model,
|
|
1088
|
+
input: text
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
const { data } = await this.http.post(path, payload);
|
|
1092
|
+
const extractPath = (_b = this.opts.embedExtractPath) != null ? _b : "data[0].embedding";
|
|
1093
|
+
const vector = resolvePath(data, extractPath);
|
|
1094
|
+
if (!Array.isArray(vector)) {
|
|
1095
|
+
throw new Error(`[UniversalLLMAdapter] Expected a number array at '${extractPath}' for embeddings.`);
|
|
1096
|
+
}
|
|
1097
|
+
return vector;
|
|
1098
|
+
}
|
|
1099
|
+
async batchEmbed(texts) {
|
|
1100
|
+
const vectors = [];
|
|
1101
|
+
for (const text of texts) {
|
|
1102
|
+
vectors.push(await this.embed(text));
|
|
1103
|
+
}
|
|
1104
|
+
return vectors;
|
|
1105
|
+
}
|
|
1106
|
+
async ping() {
|
|
1107
|
+
try {
|
|
1108
|
+
if (this.opts.pingPath) {
|
|
1109
|
+
await this.http.get(this.opts.pingPath);
|
|
1110
|
+
}
|
|
1111
|
+
return true;
|
|
1112
|
+
} catch (err) {
|
|
1113
|
+
console.error("[UniversalLLMAdapter] Ping failed:", err);
|
|
1114
|
+
return false;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
};
|
|
1118
|
+
|
|
1119
|
+
// src/llm/LLMFactory.ts
|
|
1120
|
+
var LLMFactory = class _LLMFactory {
|
|
1121
|
+
static create(llmConfig, embeddingConfig) {
|
|
1122
|
+
var _a;
|
|
1123
|
+
switch (llmConfig.provider) {
|
|
1124
|
+
case "openai":
|
|
1125
|
+
return new OpenAIProvider(llmConfig, embeddingConfig);
|
|
1126
|
+
case "anthropic":
|
|
1127
|
+
return new AnthropicProvider(llmConfig, embeddingConfig);
|
|
1128
|
+
case "ollama":
|
|
1129
|
+
return new OllamaProvider(llmConfig, embeddingConfig);
|
|
1130
|
+
case "rest":
|
|
1131
|
+
case "universal_rest":
|
|
1132
|
+
case "custom":
|
|
1133
|
+
return new UniversalLLMAdapter(llmConfig);
|
|
1134
|
+
default:
|
|
1135
|
+
if (llmConfig.baseUrl || ((_a = llmConfig.options) == null ? void 0 : _a.baseUrl)) {
|
|
1136
|
+
return new UniversalLLMAdapter(llmConfig);
|
|
1137
|
+
}
|
|
1138
|
+
throw new Error(
|
|
1139
|
+
`[LLMFactory] Unknown provider "${llmConfig.provider}". Supported: openai | anthropic | ollama | rest | custom`
|
|
1140
|
+
);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
/**
|
|
1144
|
+
* Creates a dedicated embedding-only provider.
|
|
1145
|
+
* Useful when the LLM provider (e.g. Anthropic) doesn't support embeddings.
|
|
1146
|
+
*/
|
|
1147
|
+
static createEmbeddingProvider(embeddingConfig) {
|
|
1148
|
+
const fakeLLMConfig = {
|
|
1149
|
+
provider: embeddingConfig.provider,
|
|
1150
|
+
model: embeddingConfig.model,
|
|
1151
|
+
apiKey: embeddingConfig.apiKey,
|
|
1152
|
+
baseUrl: embeddingConfig.baseUrl,
|
|
1153
|
+
options: embeddingConfig.options
|
|
1154
|
+
};
|
|
1155
|
+
return _LLMFactory.create(fakeLLMConfig, embeddingConfig);
|
|
1156
|
+
}
|
|
1157
|
+
};
|
|
1158
|
+
|
|
695
1159
|
// src/core/ProviderRegistry.ts
|
|
696
1160
|
var ProviderRegistry = class {
|
|
697
1161
|
/**
|
|
698
|
-
* Register a custom vector provider.
|
|
1162
|
+
* Register a custom vector provider class by name.
|
|
1163
|
+
* The name must match the provider value used in VectorDBConfig.provider.
|
|
1164
|
+
*
|
|
1165
|
+
* @example
|
|
1166
|
+
* ProviderRegistry.registerVectorProvider('my-db', MyCustomProvider);
|
|
699
1167
|
*/
|
|
700
1168
|
static registerVectorProvider(name, providerClass) {
|
|
701
1169
|
this.vectorProviders[name] = providerClass;
|
|
702
1170
|
}
|
|
703
1171
|
/**
|
|
704
1172
|
* Creates a vector database provider based on the configuration.
|
|
1173
|
+
* Built-in providers are dynamically imported to avoid bundling all SDKs.
|
|
705
1174
|
*/
|
|
706
1175
|
static async createVectorProvider(config) {
|
|
707
1176
|
const { provider } = config;
|
|
@@ -709,33 +1178,48 @@ var ProviderRegistry = class {
|
|
|
709
1178
|
return new this.vectorProviders[provider](config);
|
|
710
1179
|
}
|
|
711
1180
|
switch (provider) {
|
|
712
|
-
case "pinecone":
|
|
713
|
-
const { PineconeProvider } = await import("./PineconeProvider-
|
|
1181
|
+
case "pinecone": {
|
|
1182
|
+
const { PineconeProvider } = await import("./PineconeProvider-E6L5Z2FO.mjs");
|
|
714
1183
|
return new PineconeProvider(config);
|
|
1184
|
+
}
|
|
715
1185
|
case "pgvector":
|
|
716
|
-
case "postgresql":
|
|
1186
|
+
case "postgresql": {
|
|
717
1187
|
const { PostgreSQLProvider } = await import("./PostgreSQLProvider-ZNXA67IM.mjs");
|
|
718
1188
|
return new PostgreSQLProvider(config);
|
|
719
|
-
|
|
1189
|
+
}
|
|
1190
|
+
case "mongodb": {
|
|
720
1191
|
const { MongoDBProvider } = await import("./MongoDBProvider-WWVJG3WT.mjs");
|
|
721
1192
|
return new MongoDBProvider(config);
|
|
722
|
-
|
|
723
|
-
|
|
1193
|
+
}
|
|
1194
|
+
case "milvus": {
|
|
1195
|
+
const { MilvusProvider } = await import("./MilvusProvider-CJDBCBVI.mjs");
|
|
724
1196
|
return new MilvusProvider(config);
|
|
725
|
-
|
|
726
|
-
|
|
1197
|
+
}
|
|
1198
|
+
case "qdrant": {
|
|
1199
|
+
const { QdrantProvider } = await import("./QdrantProvider-JITRNJQN.mjs");
|
|
727
1200
|
return new QdrantProvider(config);
|
|
728
|
-
|
|
729
|
-
|
|
1201
|
+
}
|
|
1202
|
+
case "chromadb": {
|
|
1203
|
+
const { ChromaDBProvider } = await import("./ChromaDBProvider-2JZSBOQX.mjs");
|
|
730
1204
|
return new ChromaDBProvider(config);
|
|
731
|
-
|
|
732
|
-
|
|
1205
|
+
}
|
|
1206
|
+
case "redis": {
|
|
1207
|
+
const { RedisProvider } = await import("./RedisProvider-3VKFQXXD.mjs");
|
|
733
1208
|
return new RedisProvider(config);
|
|
734
|
-
|
|
735
|
-
|
|
1209
|
+
}
|
|
1210
|
+
case "weaviate": {
|
|
1211
|
+
const { WeaviateProvider } = await import("./WeaviateProvider-MXIPP44J.mjs");
|
|
736
1212
|
return new WeaviateProvider(config);
|
|
1213
|
+
}
|
|
1214
|
+
case "universal_rest":
|
|
1215
|
+
case "rest": {
|
|
1216
|
+
const { UniversalVectorProvider } = await import("./UniversalVectorProvider-E6L4U4OX.mjs");
|
|
1217
|
+
return new UniversalVectorProvider(config);
|
|
1218
|
+
}
|
|
737
1219
|
default:
|
|
738
|
-
throw new Error(
|
|
1220
|
+
throw new Error(
|
|
1221
|
+
`[ProviderRegistry] Unsupported vector provider: "${provider}". Built-in providers: pinecone | pgvector | postgresql | mongodb | milvus | qdrant | chromadb | redis | weaviate | universal_rest. For custom providers, call ProviderRegistry.registerVectorProvider("${provider}", YourClass).`
|
|
1222
|
+
);
|
|
739
1223
|
}
|
|
740
1224
|
}
|
|
741
1225
|
/**
|
|
@@ -971,6 +1455,89 @@ ${errorMessages}`
|
|
|
971
1455
|
}
|
|
972
1456
|
};
|
|
973
1457
|
|
|
1458
|
+
// src/config/EmbeddingStrategy.ts
|
|
1459
|
+
var EmbeddingStrategy = /* @__PURE__ */ ((EmbeddingStrategy2) => {
|
|
1460
|
+
EmbeddingStrategy2["INTEGRATED"] = "integrated";
|
|
1461
|
+
EmbeddingStrategy2["SEPARATE"] = "separate";
|
|
1462
|
+
EmbeddingStrategy2["EXTERNAL"] = "external";
|
|
1463
|
+
return EmbeddingStrategy2;
|
|
1464
|
+
})(EmbeddingStrategy || {});
|
|
1465
|
+
var EmbeddingStrategyResolver = class {
|
|
1466
|
+
/**
|
|
1467
|
+
* Determine strategy based on LLM and embedding configs
|
|
1468
|
+
*/
|
|
1469
|
+
static determineStrategy(llmConfig, embeddingConfig) {
|
|
1470
|
+
if (!embeddingConfig) {
|
|
1471
|
+
return this.supportsEmbedding(llmConfig.provider) ? "integrated" /* INTEGRATED */ : "separate" /* SEPARATE */;
|
|
1472
|
+
}
|
|
1473
|
+
if (embeddingConfig.provider !== llmConfig.provider) {
|
|
1474
|
+
return "separate" /* SEPARATE */;
|
|
1475
|
+
}
|
|
1476
|
+
if (embeddingConfig.model !== llmConfig.model) {
|
|
1477
|
+
return "external" /* EXTERNAL */;
|
|
1478
|
+
}
|
|
1479
|
+
return "integrated" /* INTEGRATED */;
|
|
1480
|
+
}
|
|
1481
|
+
/**
|
|
1482
|
+
* Resolve and initialize providers according to the strategy
|
|
1483
|
+
*/
|
|
1484
|
+
static async resolve(llmConfig, embeddingConfig) {
|
|
1485
|
+
const strategy = this.determineStrategy(llmConfig, embeddingConfig);
|
|
1486
|
+
const llmProvider = LLMFactory.create(llmConfig, embeddingConfig);
|
|
1487
|
+
let embeddingProvider;
|
|
1488
|
+
switch (strategy) {
|
|
1489
|
+
case "integrated" /* INTEGRATED */:
|
|
1490
|
+
embeddingProvider = llmProvider;
|
|
1491
|
+
break;
|
|
1492
|
+
case "separate" /* SEPARATE */:
|
|
1493
|
+
if (!embeddingConfig) {
|
|
1494
|
+
throw new Error(
|
|
1495
|
+
"[EmbeddingStrategyResolver] SEPARATE strategy requires embeddingConfig"
|
|
1496
|
+
);
|
|
1497
|
+
}
|
|
1498
|
+
embeddingProvider = LLMFactory.createEmbeddingProvider(embeddingConfig);
|
|
1499
|
+
break;
|
|
1500
|
+
case "external" /* EXTERNAL */:
|
|
1501
|
+
if (!embeddingConfig) {
|
|
1502
|
+
throw new Error(
|
|
1503
|
+
"[EmbeddingStrategyResolver] EXTERNAL strategy requires embeddingConfig"
|
|
1504
|
+
);
|
|
1505
|
+
}
|
|
1506
|
+
embeddingProvider = LLMFactory.createEmbeddingProvider(embeddingConfig);
|
|
1507
|
+
break;
|
|
1508
|
+
default:
|
|
1509
|
+
throw new Error(`[EmbeddingStrategyResolver] Unknown strategy: ${strategy}`);
|
|
1510
|
+
}
|
|
1511
|
+
return {
|
|
1512
|
+
strategy,
|
|
1513
|
+
embeddingProvider,
|
|
1514
|
+
llmProvider
|
|
1515
|
+
};
|
|
1516
|
+
}
|
|
1517
|
+
/**
|
|
1518
|
+
* Check if an LLM provider natively supports embeddings
|
|
1519
|
+
*/
|
|
1520
|
+
static supportsEmbedding(provider) {
|
|
1521
|
+
const providersWithEmbedding = ["openai", "ollama", "rest", "universal_rest"];
|
|
1522
|
+
return providersWithEmbedding.includes(provider);
|
|
1523
|
+
}
|
|
1524
|
+
/**
|
|
1525
|
+
* Get a human-readable description of the strategy
|
|
1526
|
+
*/
|
|
1527
|
+
static getDescription(strategy) {
|
|
1528
|
+
switch (strategy) {
|
|
1529
|
+
case "integrated" /* INTEGRATED */:
|
|
1530
|
+
return "Using LLM provider for both chat and embeddings";
|
|
1531
|
+
case "separate" /* SEPARATE */:
|
|
1532
|
+
return "Using separate embedding provider";
|
|
1533
|
+
case "external" /* EXTERNAL */:
|
|
1534
|
+
return "Using external embedding service";
|
|
1535
|
+
default:
|
|
1536
|
+
return "Unknown strategy";
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
};
|
|
1540
|
+
|
|
974
1541
|
// src/core/Pipeline.ts
|
|
975
1542
|
var Pipeline = class {
|
|
976
1543
|
constructor(config) {
|
|
@@ -985,18 +1552,17 @@ var Pipeline = class {
|
|
|
985
1552
|
async initialize() {
|
|
986
1553
|
if (this.initialised) return;
|
|
987
1554
|
this.vectorDB = await ProviderRegistry.createVectorProvider(this.config.vectorDb);
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
}
|
|
1555
|
+
const { llmProvider, embeddingProvider } = await EmbeddingStrategyResolver.resolve(
|
|
1556
|
+
this.config.llm,
|
|
1557
|
+
this.config.embedding
|
|
1558
|
+
);
|
|
1559
|
+
this.llmProvider = llmProvider;
|
|
1560
|
+
this.embeddingProvider = embeddingProvider;
|
|
995
1561
|
await this.vectorDB.initialize();
|
|
996
1562
|
this.initialised = true;
|
|
997
1563
|
}
|
|
998
1564
|
/**
|
|
999
|
-
* Ingest documents with automatic chunking, embedding, and batch
|
|
1565
|
+
* Ingest documents with automatic chunking, embedding, and batch upsert.
|
|
1000
1566
|
* Handles retries for transient failures.
|
|
1001
1567
|
*/
|
|
1002
1568
|
async ingest(documents, namespace) {
|
|
@@ -1009,19 +1575,18 @@ var Pipeline = class {
|
|
|
1009
1575
|
docId: doc.docId,
|
|
1010
1576
|
metadata: doc.metadata
|
|
1011
1577
|
});
|
|
1012
|
-
const
|
|
1578
|
+
const embedBatchOptions = {
|
|
1013
1579
|
batchSize: 50,
|
|
1014
|
-
// Embedding batch size
|
|
1015
1580
|
maxRetries: 3,
|
|
1016
1581
|
initialDelayMs: 100
|
|
1017
1582
|
};
|
|
1018
1583
|
const vectors = await BatchProcessor.mapWithRetry(
|
|
1019
1584
|
chunks.map((c) => c.content),
|
|
1020
1585
|
(text) => this.embeddingProvider.embed(text),
|
|
1021
|
-
|
|
1586
|
+
embedBatchOptions
|
|
1022
1587
|
);
|
|
1023
1588
|
if (vectors.length !== chunks.length) {
|
|
1024
|
-
throw new Error(`Embedding
|
|
1589
|
+
throw new Error(`Embedding mismatch: got ${vectors.length} vectors for ${chunks.length} chunks`);
|
|
1025
1590
|
}
|
|
1026
1591
|
const upsertDocs = chunks.map((chunk, i) => ({
|
|
1027
1592
|
id: chunk.id,
|
|
@@ -1048,10 +1613,7 @@ var Pipeline = class {
|
|
|
1048
1613
|
});
|
|
1049
1614
|
} catch (error) {
|
|
1050
1615
|
console.error(`[Pipeline] Failed to ingest document ${doc.docId}:`, error);
|
|
1051
|
-
results.push({
|
|
1052
|
-
docId: doc.docId,
|
|
1053
|
-
chunksIngested: 0
|
|
1054
|
-
});
|
|
1616
|
+
results.push({ docId: doc.docId, chunksIngested: 0 });
|
|
1055
1617
|
}
|
|
1056
1618
|
}
|
|
1057
1619
|
return results;
|
|
@@ -1081,7 +1643,6 @@ ${m.content}`).join("\n\n---\n\n") : "No relevant context found.";
|
|
|
1081
1643
|
var ProviderHealthCheck = class {
|
|
1082
1644
|
/**
|
|
1083
1645
|
* Validates vector database configuration before initialization.
|
|
1084
|
-
* Performs connectivity checks and verifies required resources exist.
|
|
1085
1646
|
*/
|
|
1086
1647
|
static async checkVectorProvider(config) {
|
|
1087
1648
|
const timestamp = Date.now();
|
|
@@ -1089,7 +1650,6 @@ var ProviderHealthCheck = class {
|
|
|
1089
1650
|
projectId: "health-check",
|
|
1090
1651
|
vectorDb: config,
|
|
1091
1652
|
llm: { provider: "openai", model: "gpt-4o" },
|
|
1092
|
-
// dummy
|
|
1093
1653
|
embedding: { provider: "openai", model: "text-embedding-3-small" }
|
|
1094
1654
|
});
|
|
1095
1655
|
const vectorDbErrors = configErrors.filter((e) => e.field.startsWith("vectorDb"));
|
|
@@ -1159,10 +1719,7 @@ var ProviderHealthCheck = class {
|
|
|
1159
1719
|
return {
|
|
1160
1720
|
healthy: true,
|
|
1161
1721
|
provider: "pinecone",
|
|
1162
|
-
capabilities: {
|
|
1163
|
-
indexes: indexNames.length,
|
|
1164
|
-
targetIndex: config.indexName
|
|
1165
|
-
},
|
|
1722
|
+
capabilities: { indexes: indexNames.length, targetIndex: config.indexName },
|
|
1166
1723
|
timestamp
|
|
1167
1724
|
};
|
|
1168
1725
|
} catch (error) {
|
|
@@ -1181,9 +1738,7 @@ var ProviderHealthCheck = class {
|
|
|
1181
1738
|
const client = new Client({ connectionString: opts.connectionString });
|
|
1182
1739
|
await client.connect();
|
|
1183
1740
|
const result = await client.query(`
|
|
1184
|
-
SELECT EXISTS(
|
|
1185
|
-
SELECT 1 FROM pg_extension WHERE extname = 'vector'
|
|
1186
|
-
);
|
|
1741
|
+
SELECT EXISTS(SELECT 1 FROM pg_extension WHERE extname = 'vector');
|
|
1187
1742
|
`);
|
|
1188
1743
|
const hasVector = result.rows[0].exists;
|
|
1189
1744
|
await client.end();
|
|
@@ -1218,7 +1773,7 @@ var ProviderHealthCheck = class {
|
|
|
1218
1773
|
provider: "mongodb",
|
|
1219
1774
|
capabilities: {
|
|
1220
1775
|
collections: collectionNames.length,
|
|
1221
|
-
targetCollection: hasCollection ? opts.collection : "NOT FOUND"
|
|
1776
|
+
targetCollection: hasCollection ? opts.collection : "NOT FOUND (will be created on first insert)"
|
|
1222
1777
|
},
|
|
1223
1778
|
timestamp
|
|
1224
1779
|
};
|
|
@@ -1231,16 +1786,16 @@ var ProviderHealthCheck = class {
|
|
|
1231
1786
|
};
|
|
1232
1787
|
}
|
|
1233
1788
|
}
|
|
1789
|
+
/**
|
|
1790
|
+
* Milvus health check — uses baseUrl/uri (matching MilvusProvider constructor).
|
|
1791
|
+
*/
|
|
1234
1792
|
static async checkMilvus(config, timestamp) {
|
|
1235
1793
|
const opts = config.options;
|
|
1236
|
-
const
|
|
1237
|
-
const port = opts.port || 19530;
|
|
1794
|
+
const baseUrl = opts.baseUrl || opts.uri || (opts.host ? `http://${opts.host}:${opts.port || 19530}` : "http://localhost:19530");
|
|
1238
1795
|
try {
|
|
1239
1796
|
const controller = new AbortController();
|
|
1240
1797
|
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
1241
|
-
const response = await fetch(
|
|
1242
|
-
signal: controller.signal
|
|
1243
|
-
});
|
|
1798
|
+
const response = await fetch(`${baseUrl}/v1/health`, { signal: controller.signal });
|
|
1244
1799
|
clearTimeout(timeoutId);
|
|
1245
1800
|
if (!response.ok) {
|
|
1246
1801
|
throw new Error(`Health check returned ${response.status}`);
|
|
@@ -1248,7 +1803,7 @@ var ProviderHealthCheck = class {
|
|
|
1248
1803
|
return {
|
|
1249
1804
|
healthy: true,
|
|
1250
1805
|
provider: "milvus",
|
|
1251
|
-
capabilities: { endpoint:
|
|
1806
|
+
capabilities: { endpoint: baseUrl },
|
|
1252
1807
|
timestamp
|
|
1253
1808
|
};
|
|
1254
1809
|
} catch (error) {
|
|
@@ -1260,17 +1815,18 @@ var ProviderHealthCheck = class {
|
|
|
1260
1815
|
};
|
|
1261
1816
|
}
|
|
1262
1817
|
}
|
|
1818
|
+
/**
|
|
1819
|
+
* Qdrant health check — uses baseUrl (matching QdrantProvider constructor).
|
|
1820
|
+
*/
|
|
1263
1821
|
static async checkQdrant(config, timestamp) {
|
|
1264
1822
|
const opts = config.options;
|
|
1265
|
-
const baseUrl = opts.baseUrl || opts.url || "http://localhost:6333";
|
|
1823
|
+
const baseUrl = (opts.baseUrl || opts.url || "http://localhost:6333").replace(/\/$/, "");
|
|
1266
1824
|
try {
|
|
1267
1825
|
const apiKey = opts.apiKey;
|
|
1268
1826
|
const response = await fetch(`${baseUrl}/`, {
|
|
1269
1827
|
headers: apiKey ? { "api-key": apiKey } : {}
|
|
1270
1828
|
});
|
|
1271
|
-
if (!response.ok) {
|
|
1272
|
-
throw new Error(`Health check returned ${response.status}`);
|
|
1273
|
-
}
|
|
1829
|
+
if (!response.ok) throw new Error(`Health check returned ${response.status}`);
|
|
1274
1830
|
const health = await response.json();
|
|
1275
1831
|
return {
|
|
1276
1832
|
healthy: true,
|
|
@@ -1287,19 +1843,19 @@ var ProviderHealthCheck = class {
|
|
|
1287
1843
|
};
|
|
1288
1844
|
}
|
|
1289
1845
|
}
|
|
1846
|
+
/**
|
|
1847
|
+
* ChromaDB health check — uses baseUrl/host (matching ChromaDBProvider constructor).
|
|
1848
|
+
*/
|
|
1290
1849
|
static async checkChromaDB(config, timestamp) {
|
|
1291
1850
|
const opts = config.options;
|
|
1292
|
-
const
|
|
1293
|
-
const port = opts.port || 8e3;
|
|
1851
|
+
const baseUrl = opts.baseUrl || (opts.host ? `http://${opts.host}:${opts.port || 8e3}` : "http://localhost:8000");
|
|
1294
1852
|
try {
|
|
1295
|
-
const response = await fetch(
|
|
1296
|
-
if (!response.ok) {
|
|
1297
|
-
throw new Error(`Health check returned ${response.status}`);
|
|
1298
|
-
}
|
|
1853
|
+
const response = await fetch(`${baseUrl}/api/v1/heartbeat`);
|
|
1854
|
+
if (!response.ok) throw new Error(`Health check returned ${response.status}`);
|
|
1299
1855
|
return {
|
|
1300
1856
|
healthy: true,
|
|
1301
1857
|
provider: "chromadb",
|
|
1302
|
-
capabilities: { endpoint:
|
|
1858
|
+
capabilities: { endpoint: baseUrl },
|
|
1303
1859
|
timestamp
|
|
1304
1860
|
};
|
|
1305
1861
|
} catch (error) {
|
|
@@ -1311,38 +1867,43 @@ var ProviderHealthCheck = class {
|
|
|
1311
1867
|
};
|
|
1312
1868
|
}
|
|
1313
1869
|
}
|
|
1870
|
+
/**
|
|
1871
|
+
* Redis health check — Redis is TCP-only (no HTTP endpoint).
|
|
1872
|
+
* We report healthy=true with a note; actual connectivity is validated at first operation.
|
|
1873
|
+
*/
|
|
1314
1874
|
static async checkRedis(config, timestamp) {
|
|
1315
1875
|
const opts = config.options;
|
|
1316
|
-
const url = opts.url || `redis://${opts.host}:${opts.port}
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
healthy: true,
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
timestamp
|
|
1324
|
-
};
|
|
1325
|
-
} catch (error) {
|
|
1326
|
-
return {
|
|
1327
|
-
healthy: false,
|
|
1328
|
-
provider: "redis",
|
|
1329
|
-
error: `Connection check failed: ${error instanceof Error ? error.message : String(error)}. Ensure Redis is running at ${url}`,
|
|
1330
|
-
timestamp
|
|
1331
|
-
};
|
|
1876
|
+
const url = opts.baseUrl || opts.url || (opts.host && opts.port ? `redis://${opts.host}:${opts.port}` : "redis://localhost:6379");
|
|
1877
|
+
if (url.startsWith("http")) {
|
|
1878
|
+
try {
|
|
1879
|
+
await fetch(url);
|
|
1880
|
+
return { healthy: true, provider: "redis", capabilities: { endpoint: url }, timestamp };
|
|
1881
|
+
} catch (e) {
|
|
1882
|
+
}
|
|
1332
1883
|
}
|
|
1884
|
+
return {
|
|
1885
|
+
healthy: true,
|
|
1886
|
+
provider: "redis",
|
|
1887
|
+
capabilities: {
|
|
1888
|
+
endpoint: url,
|
|
1889
|
+
note: "Redis uses TCP; connectivity is validated on first operation."
|
|
1890
|
+
},
|
|
1891
|
+
timestamp
|
|
1892
|
+
};
|
|
1333
1893
|
}
|
|
1894
|
+
/**
|
|
1895
|
+
* Weaviate health check — uses baseUrl/url (matching WeaviateProvider constructor).
|
|
1896
|
+
*/
|
|
1334
1897
|
static async checkWeaviate(config, timestamp) {
|
|
1335
1898
|
const opts = config.options;
|
|
1336
|
-
const
|
|
1899
|
+
const baseUrl = (opts.baseUrl || opts.url || "http://localhost:8080").replace(/\/$/, "");
|
|
1337
1900
|
try {
|
|
1338
|
-
const response = await fetch(`${
|
|
1339
|
-
if (!response.ok) {
|
|
1340
|
-
throw new Error(`Health check returned ${response.status}`);
|
|
1341
|
-
}
|
|
1901
|
+
const response = await fetch(`${baseUrl}/v1/.well-known/ready`);
|
|
1902
|
+
if (!response.ok) throw new Error(`Health check returned ${response.status}`);
|
|
1342
1903
|
return {
|
|
1343
1904
|
healthy: true,
|
|
1344
1905
|
provider: "weaviate",
|
|
1345
|
-
capabilities: { endpoint:
|
|
1906
|
+
capabilities: { endpoint: baseUrl },
|
|
1346
1907
|
timestamp
|
|
1347
1908
|
};
|
|
1348
1909
|
} catch (error) {
|
|
@@ -1358,12 +1919,7 @@ var ProviderHealthCheck = class {
|
|
|
1358
1919
|
const opts = config.options;
|
|
1359
1920
|
const baseUrl = (opts.baseUrl || "").replace(/\/$/, "");
|
|
1360
1921
|
if (!baseUrl) {
|
|
1361
|
-
return {
|
|
1362
|
-
healthy: false,
|
|
1363
|
-
provider: "rest",
|
|
1364
|
-
error: "baseUrl is required",
|
|
1365
|
-
timestamp
|
|
1366
|
-
};
|
|
1922
|
+
return { healthy: false, provider: "rest", error: "baseUrl is required", timestamp };
|
|
1367
1923
|
}
|
|
1368
1924
|
try {
|
|
1369
1925
|
const response = await fetch(`${baseUrl}/health`, {
|
|
@@ -1419,18 +1975,14 @@ var ProviderHealthCheck = class {
|
|
|
1419
1975
|
}
|
|
1420
1976
|
static async checkOpenAI(config, timestamp) {
|
|
1421
1977
|
try {
|
|
1422
|
-
const
|
|
1423
|
-
const client = new
|
|
1978
|
+
const OpenAI2 = await import("openai");
|
|
1979
|
+
const client = new OpenAI2.default({ apiKey: config.apiKey });
|
|
1424
1980
|
const models = await client.models.list();
|
|
1425
1981
|
const hasModel = models.data.some((m) => m.id === config.model);
|
|
1426
1982
|
return {
|
|
1427
1983
|
healthy: true,
|
|
1428
1984
|
provider: "openai",
|
|
1429
|
-
capabilities: {
|
|
1430
|
-
model: config.model,
|
|
1431
|
-
available: hasModel,
|
|
1432
|
-
totalModels: models.data.length
|
|
1433
|
-
},
|
|
1985
|
+
capabilities: { model: config.model, available: hasModel, totalModels: models.data.length },
|
|
1434
1986
|
timestamp
|
|
1435
1987
|
};
|
|
1436
1988
|
} catch (error) {
|
|
@@ -1444,19 +1996,14 @@ var ProviderHealthCheck = class {
|
|
|
1444
1996
|
}
|
|
1445
1997
|
static async checkAnthropic(config, timestamp) {
|
|
1446
1998
|
try {
|
|
1447
|
-
const { default:
|
|
1448
|
-
const client = new
|
|
1999
|
+
const { default: Anthropic2 } = await import("@anthropic-ai/sdk");
|
|
2000
|
+
const client = new Anthropic2({ apiKey: config.apiKey });
|
|
1449
2001
|
await client.messages.create({
|
|
1450
2002
|
model: config.model,
|
|
1451
2003
|
max_tokens: 10,
|
|
1452
2004
|
messages: [{ role: "user", content: "ping" }]
|
|
1453
2005
|
});
|
|
1454
|
-
return {
|
|
1455
|
-
healthy: true,
|
|
1456
|
-
provider: "anthropic",
|
|
1457
|
-
capabilities: { model: config.model },
|
|
1458
|
-
timestamp
|
|
1459
|
-
};
|
|
2006
|
+
return { healthy: true, provider: "anthropic", capabilities: { model: config.model }, timestamp };
|
|
1460
2007
|
} catch (error) {
|
|
1461
2008
|
return {
|
|
1462
2009
|
healthy: false,
|
|
@@ -1470,20 +2017,14 @@ var ProviderHealthCheck = class {
|
|
|
1470
2017
|
const baseUrl = (config.baseUrl || "http://localhost:11434").replace(/\/$/, "");
|
|
1471
2018
|
try {
|
|
1472
2019
|
const response = await fetch(`${baseUrl}/api/tags`);
|
|
1473
|
-
if (!response.ok) {
|
|
1474
|
-
throw new Error(`Health check returned ${response.status}`);
|
|
1475
|
-
}
|
|
2020
|
+
if (!response.ok) throw new Error(`Health check returned ${response.status}`);
|
|
1476
2021
|
const data = await response.json();
|
|
1477
2022
|
const models = data.models || [];
|
|
1478
2023
|
const hasModel = models.some((m) => m.name === config.model);
|
|
1479
2024
|
return {
|
|
1480
2025
|
healthy: true,
|
|
1481
2026
|
provider: "ollama",
|
|
1482
|
-
capabilities: {
|
|
1483
|
-
model: config.model,
|
|
1484
|
-
available: hasModel,
|
|
1485
|
-
totalModels: models.length
|
|
1486
|
-
},
|
|
2027
|
+
capabilities: { model: config.model, available: hasModel, totalModels: models.length },
|
|
1487
2028
|
timestamp
|
|
1488
2029
|
};
|
|
1489
2030
|
} catch (error) {
|
|
@@ -1500,18 +2041,11 @@ var ProviderHealthCheck = class {
|
|
|
1500
2041
|
const baseUrlRaw = config.baseUrl || ((_a = config.options) == null ? void 0 : _a.baseUrl);
|
|
1501
2042
|
const baseUrl = (typeof baseUrlRaw === "string" ? baseUrlRaw : "").replace(/\/$/, "");
|
|
1502
2043
|
if (!baseUrl) {
|
|
1503
|
-
return {
|
|
1504
|
-
healthy: false,
|
|
1505
|
-
provider: config.provider,
|
|
1506
|
-
error: "baseUrl is required",
|
|
1507
|
-
timestamp
|
|
1508
|
-
};
|
|
2044
|
+
return { healthy: false, provider: config.provider, error: "baseUrl is required", timestamp };
|
|
1509
2045
|
}
|
|
1510
2046
|
try {
|
|
1511
2047
|
const headers = (_b = config.options) == null ? void 0 : _b.headers;
|
|
1512
|
-
const response = await fetch(`${baseUrl}/health`, {
|
|
1513
|
-
headers: headers || void 0
|
|
1514
|
-
});
|
|
2048
|
+
const response = await fetch(`${baseUrl}/health`, { headers: headers || void 0 });
|
|
1515
2049
|
return {
|
|
1516
2050
|
healthy: response.ok,
|
|
1517
2051
|
provider: config.provider,
|
|
@@ -1528,7 +2062,7 @@ var ProviderHealthCheck = class {
|
|
|
1528
2062
|
}
|
|
1529
2063
|
}
|
|
1530
2064
|
/**
|
|
1531
|
-
* Runs comprehensive health checks on all configured providers.
|
|
2065
|
+
* Runs comprehensive health checks on all configured providers in parallel.
|
|
1532
2066
|
*/
|
|
1533
2067
|
static async checkAll(vectorDbConfig, llmConfig, embeddingConfig) {
|
|
1534
2068
|
const [vectorDb, llm, embedding] = await Promise.all([
|
|
@@ -1753,10 +2287,23 @@ function createUploadHandler(config) {
|
|
|
1753
2287
|
export {
|
|
1754
2288
|
getRagConfig,
|
|
1755
2289
|
ConfigResolver,
|
|
2290
|
+
ConfigValidator,
|
|
1756
2291
|
DocumentChunker,
|
|
2292
|
+
OpenAIProvider,
|
|
2293
|
+
AnthropicProvider,
|
|
2294
|
+
OllamaProvider,
|
|
2295
|
+
LLM_PROFILES,
|
|
2296
|
+
VECTOR_PROFILES,
|
|
2297
|
+
UniversalLLMAdapter,
|
|
2298
|
+
LLMFactory,
|
|
1757
2299
|
ProviderRegistry,
|
|
2300
|
+
BatchProcessor,
|
|
2301
|
+
EmbeddingStrategy,
|
|
2302
|
+
EmbeddingStrategyResolver,
|
|
1758
2303
|
Pipeline,
|
|
2304
|
+
ProviderHealthCheck,
|
|
1759
2305
|
VectorPlugin,
|
|
2306
|
+
DocumentParser,
|
|
1760
2307
|
createChatHandler,
|
|
1761
2308
|
createIngestHandler,
|
|
1762
2309
|
createHealthHandler,
|