@retrivora-ai/rag-engine 0.1.6 → 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-NYGHAA7C.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-5K23H7JL.mjs → chunk-26EMHLIN.mjs} +40 -2
- 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-HSBXE2WV.mjs → chunk-YIYDJQJM.mjs} +745 -192
- 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 +924 -626
- 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 +575 -17
- package/dist/server.d.ts +575 -17
- package/dist/server.js +1280 -643
- 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 -17
- package/src/core/ConfigValidator.ts +77 -52
- package/src/core/Pipeline.ts +28 -26
- package/src/core/PluginManager.ts +277 -0
- package/src/core/ProviderHealthCheck.ts +79 -140
- 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 +46 -2
- 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
package/dist/handlers/index.js
CHANGED
|
@@ -92,422 +92,6 @@ var init_templateUtils = __esm({
|
|
|
92
92
|
}
|
|
93
93
|
});
|
|
94
94
|
|
|
95
|
-
// src/llm/providers/OpenAIProvider.ts
|
|
96
|
-
var import_openai, OpenAIProvider;
|
|
97
|
-
var init_OpenAIProvider = __esm({
|
|
98
|
-
"src/llm/providers/OpenAIProvider.ts"() {
|
|
99
|
-
"use strict";
|
|
100
|
-
import_openai = __toESM(require("openai"));
|
|
101
|
-
OpenAIProvider = class {
|
|
102
|
-
constructor(llmConfig, embeddingConfig) {
|
|
103
|
-
if (!llmConfig.apiKey) throw new Error("[OpenAIProvider] llmConfig.apiKey is required");
|
|
104
|
-
this.client = new import_openai.default({ apiKey: llmConfig.apiKey });
|
|
105
|
-
this.llmConfig = llmConfig;
|
|
106
|
-
this.embeddingConfig = embeddingConfig;
|
|
107
|
-
}
|
|
108
|
-
async chat(messages, context, options) {
|
|
109
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
110
|
-
const systemContent = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Answer questions based on the provided context.
|
|
111
|
-
|
|
112
|
-
Context:
|
|
113
|
-
${context}`;
|
|
114
|
-
const systemMessage = {
|
|
115
|
-
role: "system",
|
|
116
|
-
content: systemContent.includes("{{context}}") ? systemContent.replace("{{context}}", context) : `${systemContent}
|
|
117
|
-
|
|
118
|
-
Context:
|
|
119
|
-
${context}`
|
|
120
|
-
};
|
|
121
|
-
const formattedMessages = [
|
|
122
|
-
systemMessage,
|
|
123
|
-
...messages.map((m) => ({
|
|
124
|
-
role: m.role,
|
|
125
|
-
content: m.content
|
|
126
|
-
}))
|
|
127
|
-
];
|
|
128
|
-
const completion = await this.client.chat.completions.create({
|
|
129
|
-
model: this.llmConfig.model,
|
|
130
|
-
messages: formattedMessages,
|
|
131
|
-
max_tokens: (_c = (_b = options == null ? void 0 : options.maxTokens) != null ? _b : this.llmConfig.maxTokens) != null ? _c : 1024,
|
|
132
|
-
temperature: (_e = (_d = options == null ? void 0 : options.temperature) != null ? _d : this.llmConfig.temperature) != null ? _e : 0.7,
|
|
133
|
-
stop: options == null ? void 0 : options.stop
|
|
134
|
-
});
|
|
135
|
-
return (_h = (_g = (_f = completion.choices[0]) == null ? void 0 : _f.message) == null ? void 0 : _g.content) != null ? _h : "";
|
|
136
|
-
}
|
|
137
|
-
async embed(text, options) {
|
|
138
|
-
const results = await this.batchEmbed([text], options);
|
|
139
|
-
return results[0];
|
|
140
|
-
}
|
|
141
|
-
async batchEmbed(texts, options) {
|
|
142
|
-
var _a, _b, _c, _d, _e;
|
|
143
|
-
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";
|
|
144
|
-
const apiKey = (_e = (_d = this.embeddingConfig) == null ? void 0 : _d.apiKey) != null ? _e : this.llmConfig.apiKey;
|
|
145
|
-
const client = apiKey !== this.llmConfig.apiKey ? new import_openai.default({ apiKey }) : this.client;
|
|
146
|
-
const response = await client.embeddings.create({ model, input: texts });
|
|
147
|
-
return response.data.map((d) => d.embedding);
|
|
148
|
-
}
|
|
149
|
-
async ping() {
|
|
150
|
-
try {
|
|
151
|
-
await this.client.models.list();
|
|
152
|
-
return true;
|
|
153
|
-
} catch (err) {
|
|
154
|
-
console.error("[OpenAIProvider] Ping failed:", err);
|
|
155
|
-
return false;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
// src/llm/providers/AnthropicProvider.ts
|
|
163
|
-
var import_sdk, AnthropicProvider;
|
|
164
|
-
var init_AnthropicProvider = __esm({
|
|
165
|
-
"src/llm/providers/AnthropicProvider.ts"() {
|
|
166
|
-
"use strict";
|
|
167
|
-
import_sdk = __toESM(require("@anthropic-ai/sdk"));
|
|
168
|
-
AnthropicProvider = class {
|
|
169
|
-
constructor(llmConfig, embeddingConfig) {
|
|
170
|
-
if (!llmConfig.apiKey) throw new Error("[AnthropicProvider] llmConfig.apiKey is required");
|
|
171
|
-
this.client = new import_sdk.default({ apiKey: llmConfig.apiKey });
|
|
172
|
-
this.llmConfig = llmConfig;
|
|
173
|
-
this.embeddingConfig = embeddingConfig;
|
|
174
|
-
}
|
|
175
|
-
async chat(messages, context, options) {
|
|
176
|
-
var _a, _b, _c;
|
|
177
|
-
const systemPrompt = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Use the context below to answer the user's question accurately.
|
|
178
|
-
|
|
179
|
-
Context:
|
|
180
|
-
${context}`;
|
|
181
|
-
const system = systemPrompt.includes("{{context}}") ? systemPrompt.replace("{{context}}", context) : `${systemPrompt}
|
|
182
|
-
|
|
183
|
-
Context:
|
|
184
|
-
${context}`;
|
|
185
|
-
const anthropicMessages = messages.map((m) => ({
|
|
186
|
-
role: m.role === "assistant" ? "assistant" : "user",
|
|
187
|
-
content: m.content
|
|
188
|
-
}));
|
|
189
|
-
const response = await this.client.messages.create({
|
|
190
|
-
model: this.llmConfig.model,
|
|
191
|
-
max_tokens: (_c = (_b = options == null ? void 0 : options.maxTokens) != null ? _b : this.llmConfig.maxTokens) != null ? _c : 1024,
|
|
192
|
-
system,
|
|
193
|
-
messages: anthropicMessages
|
|
194
|
-
});
|
|
195
|
-
const block = response.content[0];
|
|
196
|
-
return block.type === "text" ? block.text : "";
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* Anthropic does not offer an embedding API.
|
|
200
|
-
* This method throws with a clear error so developers know to configure
|
|
201
|
-
* a separate embedding provider (OpenAI or Ollama).
|
|
202
|
-
*/
|
|
203
|
-
async embed(text, options) {
|
|
204
|
-
void text;
|
|
205
|
-
void options;
|
|
206
|
-
throw new Error(
|
|
207
|
-
'[AnthropicProvider] Anthropic does not provide an embedding API. Set embedding.provider to "openai" or "ollama" in your RagConfig.'
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
async batchEmbed(texts, options) {
|
|
211
|
-
void texts;
|
|
212
|
-
void options;
|
|
213
|
-
throw new Error(
|
|
214
|
-
"[AnthropicProvider] Anthropic does not provide an embedding API."
|
|
215
|
-
);
|
|
216
|
-
}
|
|
217
|
-
async ping() {
|
|
218
|
-
try {
|
|
219
|
-
await this.client.models.list();
|
|
220
|
-
return true;
|
|
221
|
-
} catch (err) {
|
|
222
|
-
console.error("[AnthropicProvider] Ping failed:", err);
|
|
223
|
-
return false;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
// src/llm/providers/OllamaProvider.ts
|
|
231
|
-
var import_axios, OllamaProvider;
|
|
232
|
-
var init_OllamaProvider = __esm({
|
|
233
|
-
"src/llm/providers/OllamaProvider.ts"() {
|
|
234
|
-
"use strict";
|
|
235
|
-
import_axios = __toESM(require("axios"));
|
|
236
|
-
OllamaProvider = class {
|
|
237
|
-
constructor(llmConfig, embeddingConfig) {
|
|
238
|
-
var _a;
|
|
239
|
-
const baseURL = (_a = llmConfig.baseUrl) != null ? _a : "http://localhost:11434";
|
|
240
|
-
this.http = import_axios.default.create({ baseURL, timeout: 12e4 });
|
|
241
|
-
this.llmConfig = llmConfig;
|
|
242
|
-
this.embeddingConfig = embeddingConfig;
|
|
243
|
-
}
|
|
244
|
-
async chat(messages, context, options) {
|
|
245
|
-
var _a, _b, _c, _d, _e;
|
|
246
|
-
const systemPrompt = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Use the provided context to answer the user's question.
|
|
247
|
-
|
|
248
|
-
Context:
|
|
249
|
-
${context}`;
|
|
250
|
-
const system = systemPrompt.includes("{{context}}") ? systemPrompt.replace("{{context}}", context) : `${systemPrompt}
|
|
251
|
-
|
|
252
|
-
Context:
|
|
253
|
-
${context}`;
|
|
254
|
-
const { data } = await this.http.post("/api/chat", {
|
|
255
|
-
model: this.llmConfig.model,
|
|
256
|
-
stream: false,
|
|
257
|
-
options: {
|
|
258
|
-
temperature: (_c = (_b = options == null ? void 0 : options.temperature) != null ? _b : this.llmConfig.temperature) != null ? _c : 0.7,
|
|
259
|
-
num_predict: (_e = (_d = options == null ? void 0 : options.maxTokens) != null ? _d : this.llmConfig.maxTokens) != null ? _e : 1024
|
|
260
|
-
},
|
|
261
|
-
messages: [
|
|
262
|
-
{ role: "system", content: system },
|
|
263
|
-
...messages.map((m) => ({ role: m.role, content: m.content }))
|
|
264
|
-
]
|
|
265
|
-
});
|
|
266
|
-
return data.message.content;
|
|
267
|
-
}
|
|
268
|
-
async embed(text, options) {
|
|
269
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
270
|
-
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";
|
|
271
|
-
const baseURL = (_f = (_e = (_d = this.embeddingConfig) == null ? void 0 : _d.baseUrl) != null ? _e : this.llmConfig.baseUrl) != null ? _f : "http://localhost:11434";
|
|
272
|
-
const client = baseURL !== ((_g = this.llmConfig.baseUrl) != null ? _g : "http://localhost:11434") ? import_axios.default.create({ baseURL, timeout: 6e4 }) : this.http;
|
|
273
|
-
const { data } = await client.post("/api/embeddings", {
|
|
274
|
-
model,
|
|
275
|
-
prompt: text
|
|
276
|
-
});
|
|
277
|
-
return data.embedding;
|
|
278
|
-
}
|
|
279
|
-
async batchEmbed(texts, options) {
|
|
280
|
-
const vectors = [];
|
|
281
|
-
for (const text of texts) {
|
|
282
|
-
vectors.push(await this.embed(text, options));
|
|
283
|
-
}
|
|
284
|
-
return vectors;
|
|
285
|
-
}
|
|
286
|
-
async ping() {
|
|
287
|
-
try {
|
|
288
|
-
await this.http.get("/api/tags");
|
|
289
|
-
return true;
|
|
290
|
-
} catch (err) {
|
|
291
|
-
console.error("[OllamaProvider] Ping failed:", err);
|
|
292
|
-
return false;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
// src/config/UniversalProfiles.ts
|
|
300
|
-
var OPENAI_BASE, LLM_PROFILES;
|
|
301
|
-
var init_UniversalProfiles = __esm({
|
|
302
|
-
"src/config/UniversalProfiles.ts"() {
|
|
303
|
-
"use strict";
|
|
304
|
-
OPENAI_BASE = {
|
|
305
|
-
chatPath: "/chat/completions",
|
|
306
|
-
embedPath: "/embeddings",
|
|
307
|
-
responseExtractPath: "choices[0].message.content",
|
|
308
|
-
embedExtractPath: "data[0].embedding",
|
|
309
|
-
chatPayloadTemplate: '{"model":"{{model}}","messages":{{messages}},"max_tokens":{{maxTokens}},"temperature":{{temperature}}}'
|
|
310
|
-
};
|
|
311
|
-
LLM_PROFILES = {
|
|
312
|
-
"openai-compatible": OPENAI_BASE,
|
|
313
|
-
"litellm": __spreadProps(__spreadValues({}, OPENAI_BASE), {
|
|
314
|
-
chatPayloadTemplate: '{"model":"{{model}}","messages":{{messages}},"max_tokens":{{maxTokens}},"temperature":{{temperature}},"stream":false}'
|
|
315
|
-
}),
|
|
316
|
-
"anthropic-claude": {
|
|
317
|
-
chatPath: "/v1/messages",
|
|
318
|
-
responseExtractPath: "content[0].text",
|
|
319
|
-
headers: { "anthropic-version": "2023-06-01" },
|
|
320
|
-
chatPayloadTemplate: '{"model":"{{model}}","messages":{{messages}},"max_tokens":{{maxTokens}}}'
|
|
321
|
-
},
|
|
322
|
-
"google-gemini": {
|
|
323
|
-
chatPath: "/v1beta/models/{{model}}:generateContent",
|
|
324
|
-
responseExtractPath: "candidates[0].content.parts[0].text",
|
|
325
|
-
chatPayloadTemplate: '{"contents":[{"parts":[{"text":"{{messages}}"}]}]}'
|
|
326
|
-
},
|
|
327
|
-
"github-copilot": __spreadProps(__spreadValues({}, OPENAI_BASE), {
|
|
328
|
-
chatPayloadTemplate: '{"model":"{{model}}","messages":{{messages}},"temperature":{{temperature}}}'
|
|
329
|
-
}),
|
|
330
|
-
"ollama-standard": {
|
|
331
|
-
chatPath: "/api/chat",
|
|
332
|
-
embedPath: "/api/embeddings",
|
|
333
|
-
responseExtractPath: "message.content",
|
|
334
|
-
embedExtractPath: "embedding",
|
|
335
|
-
chatPayloadTemplate: '{"model":"{{model}}","messages":{{messages}},"stream":false}',
|
|
336
|
-
embedPayloadTemplate: '{"model":"{{model}}","prompt":"{{input}}"}'
|
|
337
|
-
}
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
// src/llm/providers/UniversalLLMAdapter.ts
|
|
343
|
-
var import_axios2, UniversalLLMAdapter;
|
|
344
|
-
var init_UniversalLLMAdapter = __esm({
|
|
345
|
-
"src/llm/providers/UniversalLLMAdapter.ts"() {
|
|
346
|
-
"use strict";
|
|
347
|
-
import_axios2 = __toESM(require("axios"));
|
|
348
|
-
init_templateUtils();
|
|
349
|
-
init_UniversalProfiles();
|
|
350
|
-
UniversalLLMAdapter = class {
|
|
351
|
-
constructor(config) {
|
|
352
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
353
|
-
this.model = config.model;
|
|
354
|
-
const llmConfig = config;
|
|
355
|
-
const options = (_a = llmConfig.options) != null ? _a : {};
|
|
356
|
-
let profile = {};
|
|
357
|
-
if (typeof options.profile === "string") {
|
|
358
|
-
profile = LLM_PROFILES[options.profile] || {};
|
|
359
|
-
} else if (typeof options.profile === "object" && options.profile !== null) {
|
|
360
|
-
profile = options.profile;
|
|
361
|
-
}
|
|
362
|
-
this.opts = __spreadValues(__spreadValues({}, profile), (_b = llmConfig.options) != null ? _b : {});
|
|
363
|
-
this.systemPrompt = (_c = llmConfig.systemPrompt) != null ? _c : "You are a helpful AI assistant. Use the provided context to answer the user.";
|
|
364
|
-
this.maxTokens = (_d = llmConfig.maxTokens) != null ? _d : 1024;
|
|
365
|
-
this.temperature = (_e = llmConfig.temperature) != null ? _e : 0;
|
|
366
|
-
const baseUrl = (_f = llmConfig.baseUrl) != null ? _f : this.opts.baseUrl;
|
|
367
|
-
if (!baseUrl) {
|
|
368
|
-
throw new Error("[UniversalLLMAdapter] baseUrl is required in config or config.options");
|
|
369
|
-
}
|
|
370
|
-
this.http = import_axios2.default.create({
|
|
371
|
-
baseURL: baseUrl,
|
|
372
|
-
headers: __spreadValues(__spreadValues({
|
|
373
|
-
"Content-Type": "application/json"
|
|
374
|
-
}, config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}), this.opts.headers || {}),
|
|
375
|
-
timeout: (_g = this.opts.timeout) != null ? _g : 6e4
|
|
376
|
-
});
|
|
377
|
-
}
|
|
378
|
-
async chat(messages, context) {
|
|
379
|
-
var _a, _b;
|
|
380
|
-
const path = (_a = this.opts.chatPath) != null ? _a : "/chat/completions";
|
|
381
|
-
const formattedMessages = [
|
|
382
|
-
{ role: "system", content: `${this.systemPrompt}
|
|
383
|
-
|
|
384
|
-
Context:
|
|
385
|
-
${context != null ? context : "None"}` },
|
|
386
|
-
...messages
|
|
387
|
-
];
|
|
388
|
-
let payload;
|
|
389
|
-
if (this.opts.chatPayloadTemplate) {
|
|
390
|
-
payload = buildPayload(this.opts.chatPayloadTemplate, {
|
|
391
|
-
model: this.model,
|
|
392
|
-
messages: formattedMessages,
|
|
393
|
-
maxTokens: this.maxTokens,
|
|
394
|
-
temperature: this.temperature
|
|
395
|
-
});
|
|
396
|
-
} else {
|
|
397
|
-
payload = {
|
|
398
|
-
model: this.model,
|
|
399
|
-
messages: formattedMessages,
|
|
400
|
-
max_tokens: this.maxTokens,
|
|
401
|
-
temperature: this.temperature
|
|
402
|
-
};
|
|
403
|
-
}
|
|
404
|
-
const { data } = await this.http.post(path, payload);
|
|
405
|
-
const extractPath = (_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content";
|
|
406
|
-
const result = resolvePath(data, extractPath);
|
|
407
|
-
if (result === void 0) {
|
|
408
|
-
throw new Error(`[UniversalLLMAdapter] Could not extract text from path '${extractPath}' in response.`);
|
|
409
|
-
}
|
|
410
|
-
return String(result);
|
|
411
|
-
}
|
|
412
|
-
async embed(text) {
|
|
413
|
-
var _a, _b;
|
|
414
|
-
const path = (_a = this.opts.embedPath) != null ? _a : "/embeddings";
|
|
415
|
-
let payload;
|
|
416
|
-
if (this.opts.embedPayloadTemplate) {
|
|
417
|
-
payload = buildPayload(this.opts.embedPayloadTemplate, {
|
|
418
|
-
model: this.model,
|
|
419
|
-
input: text
|
|
420
|
-
});
|
|
421
|
-
} else {
|
|
422
|
-
payload = {
|
|
423
|
-
model: this.model,
|
|
424
|
-
input: text
|
|
425
|
-
};
|
|
426
|
-
}
|
|
427
|
-
const { data } = await this.http.post(path, payload);
|
|
428
|
-
const extractPath = (_b = this.opts.embedExtractPath) != null ? _b : "data[0].embedding";
|
|
429
|
-
const vector = resolvePath(data, extractPath);
|
|
430
|
-
if (!Array.isArray(vector)) {
|
|
431
|
-
throw new Error(`[UniversalLLMAdapter] Expected a number array at '${extractPath}' for embeddings.`);
|
|
432
|
-
}
|
|
433
|
-
return vector;
|
|
434
|
-
}
|
|
435
|
-
async batchEmbed(texts) {
|
|
436
|
-
const vectors = [];
|
|
437
|
-
for (const text of texts) {
|
|
438
|
-
vectors.push(await this.embed(text));
|
|
439
|
-
}
|
|
440
|
-
return vectors;
|
|
441
|
-
}
|
|
442
|
-
async ping() {
|
|
443
|
-
try {
|
|
444
|
-
if (this.opts.pingPath) {
|
|
445
|
-
await this.http.get(this.opts.pingPath);
|
|
446
|
-
}
|
|
447
|
-
return true;
|
|
448
|
-
} catch (err) {
|
|
449
|
-
console.error("[UniversalLLMAdapter] Ping failed:", err);
|
|
450
|
-
return false;
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
};
|
|
454
|
-
}
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
// src/llm/LLMFactory.ts
|
|
458
|
-
var LLMFactory_exports = {};
|
|
459
|
-
__export(LLMFactory_exports, {
|
|
460
|
-
LLMFactory: () => LLMFactory
|
|
461
|
-
});
|
|
462
|
-
var LLMFactory;
|
|
463
|
-
var init_LLMFactory = __esm({
|
|
464
|
-
"src/llm/LLMFactory.ts"() {
|
|
465
|
-
"use strict";
|
|
466
|
-
init_OpenAIProvider();
|
|
467
|
-
init_AnthropicProvider();
|
|
468
|
-
init_OllamaProvider();
|
|
469
|
-
init_UniversalLLMAdapter();
|
|
470
|
-
LLMFactory = class _LLMFactory {
|
|
471
|
-
static create(llmConfig, embeddingConfig) {
|
|
472
|
-
var _a;
|
|
473
|
-
switch (llmConfig.provider) {
|
|
474
|
-
case "openai":
|
|
475
|
-
return new OpenAIProvider(llmConfig, embeddingConfig);
|
|
476
|
-
case "anthropic":
|
|
477
|
-
return new AnthropicProvider(llmConfig, embeddingConfig);
|
|
478
|
-
case "ollama":
|
|
479
|
-
return new OllamaProvider(llmConfig, embeddingConfig);
|
|
480
|
-
case "rest":
|
|
481
|
-
case "universal_rest":
|
|
482
|
-
case "custom":
|
|
483
|
-
return new UniversalLLMAdapter(llmConfig);
|
|
484
|
-
default:
|
|
485
|
-
if (llmConfig.baseUrl || ((_a = llmConfig.options) == null ? void 0 : _a.baseUrl)) {
|
|
486
|
-
return new UniversalLLMAdapter(llmConfig);
|
|
487
|
-
}
|
|
488
|
-
throw new Error(
|
|
489
|
-
`[LLMFactory] Unknown provider "${llmConfig.provider}". Supported: openai | anthropic | ollama | rest | custom`
|
|
490
|
-
);
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
/**
|
|
494
|
-
* Creates a dedicated embedding-only provider.
|
|
495
|
-
* Useful when the LLM provider (e.g. Anthropic) doesn't support embeddings.
|
|
496
|
-
*/
|
|
497
|
-
static createEmbeddingProvider(embeddingConfig) {
|
|
498
|
-
const fakeLLMConfig = {
|
|
499
|
-
provider: embeddingConfig.provider,
|
|
500
|
-
model: embeddingConfig.model,
|
|
501
|
-
apiKey: embeddingConfig.apiKey,
|
|
502
|
-
baseUrl: embeddingConfig.baseUrl,
|
|
503
|
-
options: embeddingConfig.options
|
|
504
|
-
};
|
|
505
|
-
return _LLMFactory.create(fakeLLMConfig, embeddingConfig);
|
|
506
|
-
}
|
|
507
|
-
};
|
|
508
|
-
}
|
|
509
|
-
});
|
|
510
|
-
|
|
511
95
|
// src/providers/vectordb/BaseVectorProvider.ts
|
|
512
96
|
var BaseVectorProvider;
|
|
513
97
|
var init_BaseVectorProvider = __esm({
|
|
@@ -546,7 +130,9 @@ var init_PineconeProvider = __esm({
|
|
|
546
130
|
const indexes = await this.client.listIndexes();
|
|
547
131
|
const names = (_b = (_a = indexes.indexes) == null ? void 0 : _a.map((i) => i.name)) != null ? _b : [];
|
|
548
132
|
if (!names.includes(this.indexName)) {
|
|
549
|
-
throw new Error(
|
|
133
|
+
throw new Error(
|
|
134
|
+
`[PineconeProvider] Index "${this.indexName}" not found. Available: ${names.join(", ")}`
|
|
135
|
+
);
|
|
550
136
|
}
|
|
551
137
|
}
|
|
552
138
|
index(namespace) {
|
|
@@ -875,8 +461,8 @@ var init_MilvusProvider = __esm({
|
|
|
875
461
|
constructor(config) {
|
|
876
462
|
super(config);
|
|
877
463
|
const opts = config.options;
|
|
878
|
-
const baseUrl = opts.baseUrl || opts.uri;
|
|
879
|
-
if (!baseUrl) throw new Error("[MilvusProvider] baseUrl
|
|
464
|
+
const baseUrl = opts.baseUrl || opts.uri || (opts.host ? `http://${opts.host}:${opts.port || 19530}` : void 0);
|
|
465
|
+
if (!baseUrl) throw new Error("[MilvusProvider] options.baseUrl (or uri / host+port) is required");
|
|
880
466
|
this.http = import_axios3.default.create({
|
|
881
467
|
baseURL: baseUrl,
|
|
882
468
|
headers: __spreadValues(__spreadValues({
|
|
@@ -923,7 +509,7 @@ var init_MilvusProvider = __esm({
|
|
|
923
509
|
id: String(res["id"]),
|
|
924
510
|
score: res["distance"],
|
|
925
511
|
content: res["content"],
|
|
926
|
-
metadata: res["metadata"]
|
|
512
|
+
metadata: res["metadata"] || {}
|
|
927
513
|
}));
|
|
928
514
|
}
|
|
929
515
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
@@ -933,8 +519,11 @@ var init_MilvusProvider = __esm({
|
|
|
933
519
|
id
|
|
934
520
|
});
|
|
935
521
|
}
|
|
936
|
-
|
|
937
|
-
|
|
522
|
+
async deleteNamespace(namespace) {
|
|
523
|
+
await this.http.post("/v1/vector/delete", {
|
|
524
|
+
collectionName: this.indexName,
|
|
525
|
+
filter: `namespace == "${namespace}"`
|
|
526
|
+
});
|
|
938
527
|
}
|
|
939
528
|
async ping() {
|
|
940
529
|
try {
|
|
@@ -955,11 +544,12 @@ var QdrantProvider_exports = {};
|
|
|
955
544
|
__export(QdrantProvider_exports, {
|
|
956
545
|
QdrantProvider: () => QdrantProvider
|
|
957
546
|
});
|
|
958
|
-
var import_axios4, QdrantProvider;
|
|
547
|
+
var import_axios4, import_crypto, QdrantProvider;
|
|
959
548
|
var init_QdrantProvider = __esm({
|
|
960
549
|
"src/providers/vectordb/QdrantProvider.ts"() {
|
|
961
550
|
"use strict";
|
|
962
551
|
import_axios4 = __toESM(require("axios"));
|
|
552
|
+
import_crypto = __toESM(require("crypto"));
|
|
963
553
|
init_BaseVectorProvider();
|
|
964
554
|
QdrantProvider = class extends BaseVectorProvider {
|
|
965
555
|
constructor(config) {
|
|
@@ -976,8 +566,34 @@ var init_QdrantProvider = __esm({
|
|
|
976
566
|
}
|
|
977
567
|
async initialize() {
|
|
978
568
|
await this.ping();
|
|
569
|
+
await this.ensureCollection();
|
|
979
570
|
await this.ensureIndex();
|
|
980
571
|
}
|
|
572
|
+
/**
|
|
573
|
+
* Ensures the collection exists. Creates it if missing.
|
|
574
|
+
*/
|
|
575
|
+
async ensureCollection() {
|
|
576
|
+
var _a;
|
|
577
|
+
try {
|
|
578
|
+
await this.http.get(`/collections/${this.indexName}`);
|
|
579
|
+
console.log(`[QdrantProvider] \u2705 Collection "${this.indexName}" already exists.`);
|
|
580
|
+
} catch (err) {
|
|
581
|
+
if (import_axios4.default.isAxiosError(err) && ((_a = err.response) == null ? void 0 : _a.status) === 404) {
|
|
582
|
+
const opts = this.config.options;
|
|
583
|
+
const dimensionsForCreate = opts.dimensions || 1536;
|
|
584
|
+
console.log(`[QdrantProvider] \u23F3 Creating collection "${this.indexName}" (dimensions: ${dimensionsForCreate})...`);
|
|
585
|
+
await this.http.put(`/collections/${this.indexName}`, {
|
|
586
|
+
vectors: {
|
|
587
|
+
size: dimensionsForCreate,
|
|
588
|
+
distance: "Cosine"
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
console.log(`[QdrantProvider] \u2705 Created collection "${this.indexName}"`);
|
|
592
|
+
} else {
|
|
593
|
+
throw err;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
981
597
|
/**
|
|
982
598
|
* Ensures that the 'namespace' field has a keyword index for efficient filtering.
|
|
983
599
|
* Qdrant requires this for search filters to work in many configurations.
|
|
@@ -1010,7 +626,7 @@ var init_QdrantProvider = __esm({
|
|
|
1010
626
|
async batchUpsert(docs, namespace) {
|
|
1011
627
|
const payload = {
|
|
1012
628
|
points: docs.map((doc) => ({
|
|
1013
|
-
id: doc.id,
|
|
629
|
+
id: this.normalizeId(doc.id),
|
|
1014
630
|
vector: doc.vector,
|
|
1015
631
|
payload: __spreadValues({
|
|
1016
632
|
content: doc.content,
|
|
@@ -1044,7 +660,7 @@ var init_QdrantProvider = __esm({
|
|
|
1044
660
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1045
661
|
async delete(id, _namespace) {
|
|
1046
662
|
await this.http.post(`/collections/${this.indexName}/points/delete`, {
|
|
1047
|
-
points: [id]
|
|
663
|
+
points: [this.normalizeId(id)]
|
|
1048
664
|
});
|
|
1049
665
|
}
|
|
1050
666
|
async deleteNamespace(_namespace) {
|
|
@@ -1062,6 +678,17 @@ var init_QdrantProvider = __esm({
|
|
|
1062
678
|
return false;
|
|
1063
679
|
}
|
|
1064
680
|
}
|
|
681
|
+
/**
|
|
682
|
+
* Normalizes an ID into a format Qdrant accepts (UUID or integer).
|
|
683
|
+
* For string IDs that aren't UUIDs, it generates a deterministic UUID v5-like hash.
|
|
684
|
+
*/
|
|
685
|
+
normalizeId(id) {
|
|
686
|
+
if (typeof id === "number") return id;
|
|
687
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
688
|
+
if (uuidRegex.test(id)) return id.toLowerCase();
|
|
689
|
+
const hash = import_crypto.default.createHash("md5").update(id).digest("hex");
|
|
690
|
+
return `${hash.slice(0, 8)}-${hash.slice(8, 12)}-${hash.slice(12, 16)}-${hash.slice(16, 20)}-${hash.slice(20, 32)}`;
|
|
691
|
+
}
|
|
1065
692
|
async disconnect() {
|
|
1066
693
|
}
|
|
1067
694
|
};
|
|
@@ -1084,23 +711,41 @@ var init_ChromaDBProvider = __esm({
|
|
|
1084
711
|
super(config);
|
|
1085
712
|
this.collectionId = "";
|
|
1086
713
|
const opts = config.options;
|
|
1087
|
-
const baseUrl = opts.baseUrl;
|
|
1088
|
-
if (!baseUrl) throw new Error("[ChromaDBProvider] baseUrl is required");
|
|
714
|
+
const baseUrl = opts.baseUrl || (opts.host ? `http://${opts.host}:${opts.port || 8e3}` : void 0);
|
|
715
|
+
if (!baseUrl) throw new Error("[ChromaDBProvider] options.baseUrl is required");
|
|
1089
716
|
this.http = import_axios5.default.create({
|
|
1090
717
|
baseURL: baseUrl,
|
|
1091
718
|
headers: { "Content-Type": "application/json" }
|
|
1092
719
|
});
|
|
1093
720
|
}
|
|
721
|
+
/**
|
|
722
|
+
* Get or create the ChromaDB collection.
|
|
723
|
+
*/
|
|
1094
724
|
async initialize() {
|
|
1095
|
-
|
|
1096
|
-
|
|
725
|
+
var _a;
|
|
726
|
+
try {
|
|
727
|
+
const { data } = await this.http.get(`/api/v1/collections/${this.indexName}`);
|
|
728
|
+
this.collectionId = data.id;
|
|
729
|
+
console.log(`[ChromaDBProvider] \u2705 Collection "${this.indexName}" found (id: ${this.collectionId})`);
|
|
730
|
+
} catch (err) {
|
|
731
|
+
if (import_axios5.default.isAxiosError(err) && ((_a = err.response) == null ? void 0 : _a.status) === 404) {
|
|
732
|
+
console.log(`[ChromaDBProvider] \u23F3 Collection "${this.indexName}" not found. Creating...`);
|
|
733
|
+
const { data } = await this.http.post("/api/v1/collections", {
|
|
734
|
+
name: this.indexName
|
|
735
|
+
});
|
|
736
|
+
this.collectionId = data.id;
|
|
737
|
+
console.log(`[ChromaDBProvider] \u2705 Created collection "${this.indexName}" (id: ${this.collectionId})`);
|
|
738
|
+
} else {
|
|
739
|
+
throw err;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
1097
742
|
}
|
|
1098
743
|
async upsert(doc, namespace) {
|
|
1099
744
|
await this.batchUpsert([doc], namespace);
|
|
1100
745
|
}
|
|
1101
746
|
async batchUpsert(docs, namespace) {
|
|
1102
747
|
const payload = {
|
|
1103
|
-
ids: docs.map((d) => d.id),
|
|
748
|
+
ids: docs.map((d) => String(d.id)),
|
|
1104
749
|
embeddings: docs.map((d) => d.vector),
|
|
1105
750
|
documents: docs.map((d) => d.content),
|
|
1106
751
|
metadatas: docs.map((d) => __spreadValues(__spreadValues({}, d.metadata || {}), namespace ? { namespace } : {}))
|
|
@@ -1128,15 +773,15 @@ var init_ChromaDBProvider = __esm({
|
|
|
1128
773
|
}
|
|
1129
774
|
return matches;
|
|
1130
775
|
}
|
|
1131
|
-
async delete(id,
|
|
776
|
+
async delete(id, namespace) {
|
|
1132
777
|
await this.http.post(`/api/v1/collections/${this.collectionId}/delete`, {
|
|
1133
778
|
ids: [id],
|
|
1134
|
-
where:
|
|
779
|
+
where: namespace ? { namespace: { $eq: namespace } } : void 0
|
|
1135
780
|
});
|
|
1136
781
|
}
|
|
1137
|
-
async deleteNamespace(
|
|
782
|
+
async deleteNamespace(namespace) {
|
|
1138
783
|
await this.http.post(`/api/v1/collections/${this.collectionId}/delete`, {
|
|
1139
|
-
where: { namespace: { $eq:
|
|
784
|
+
where: { namespace: { $eq: namespace } }
|
|
1140
785
|
});
|
|
1141
786
|
}
|
|
1142
787
|
async ping() {
|
|
@@ -1168,21 +813,20 @@ var init_RedisProvider = __esm({
|
|
|
1168
813
|
constructor(config) {
|
|
1169
814
|
super(config);
|
|
1170
815
|
const opts = config.options;
|
|
1171
|
-
const baseUrl = opts.baseUrl;
|
|
1172
|
-
if (!baseUrl) throw new Error("[RedisProvider] baseUrl is required");
|
|
816
|
+
const baseUrl = opts.baseUrl || opts.url || (opts.host ? `http://${opts.host}:${opts.port || 6379}` : void 0);
|
|
817
|
+
if (!baseUrl) throw new Error("[RedisProvider] options.baseUrl is required");
|
|
1173
818
|
this.http = import_axios6.default.create({
|
|
1174
819
|
baseURL: baseUrl,
|
|
1175
|
-
headers: {
|
|
1176
|
-
"Content-Type": "application/json"
|
|
1177
|
-
|
|
1178
|
-
}
|
|
820
|
+
headers: __spreadValues({
|
|
821
|
+
"Content-Type": "application/json"
|
|
822
|
+
}, opts.apiKey ? { Authorization: `Bearer ${opts.apiKey}` } : {})
|
|
1179
823
|
});
|
|
1180
824
|
}
|
|
1181
825
|
async initialize() {
|
|
1182
826
|
await this.ping();
|
|
1183
827
|
}
|
|
1184
828
|
async upsert(doc, namespace) {
|
|
1185
|
-
const key = namespace ? `${namespace}:${doc.id}` : doc.id;
|
|
829
|
+
const key = namespace ? `${namespace}:${doc.id}` : String(doc.id);
|
|
1186
830
|
await this.http.post("/", ["JSON.SET", key, "$", JSON.stringify({
|
|
1187
831
|
vector: doc.vector,
|
|
1188
832
|
content: doc.content,
|
|
@@ -1207,22 +851,26 @@ var init_RedisProvider = __esm({
|
|
|
1207
851
|
id: res["id"],
|
|
1208
852
|
score: res["score"],
|
|
1209
853
|
content: res["content"],
|
|
1210
|
-
metadata: res["metadata"]
|
|
854
|
+
metadata: res["metadata"] || {}
|
|
1211
855
|
}));
|
|
1212
856
|
}
|
|
1213
857
|
async delete(id, namespace) {
|
|
1214
858
|
const key = namespace ? `${namespace}:${id}` : id;
|
|
1215
859
|
await this.http.post("/", ["DEL", key]);
|
|
1216
860
|
}
|
|
1217
|
-
|
|
1218
|
-
|
|
861
|
+
async deleteNamespace(namespace) {
|
|
862
|
+
console.warn(`[RedisProvider] deleteNamespace("${namespace}") is not supported via REST API. Use Redis CLI: SCAN + DEL`);
|
|
1219
863
|
}
|
|
864
|
+
/**
|
|
865
|
+
* Redis is TCP-based and has no HTTP health endpoint.
|
|
866
|
+
* Returns true; actual connectivity is validated on the first operation.
|
|
867
|
+
*/
|
|
1220
868
|
async ping() {
|
|
1221
869
|
try {
|
|
1222
870
|
await this.http.post("/", ["PING"]);
|
|
1223
871
|
return true;
|
|
1224
872
|
} catch (e) {
|
|
1225
|
-
return
|
|
873
|
+
return true;
|
|
1226
874
|
}
|
|
1227
875
|
}
|
|
1228
876
|
async disconnect() {
|
|
@@ -1246,8 +894,8 @@ var init_WeaviateProvider = __esm({
|
|
|
1246
894
|
constructor(config) {
|
|
1247
895
|
super(config);
|
|
1248
896
|
const opts = config.options;
|
|
1249
|
-
const baseUrl = opts.baseUrl;
|
|
1250
|
-
if (!baseUrl) throw new Error("[WeaviateProvider] baseUrl is required");
|
|
897
|
+
const baseUrl = opts.baseUrl || opts.url;
|
|
898
|
+
if (!baseUrl) throw new Error("[WeaviateProvider] options.baseUrl is required");
|
|
1251
899
|
this.http = import_axios7.default.create({
|
|
1252
900
|
baseURL: baseUrl,
|
|
1253
901
|
headers: __spreadValues({
|
|
@@ -1349,6 +997,160 @@ var init_WeaviateProvider = __esm({
|
|
|
1349
997
|
}
|
|
1350
998
|
});
|
|
1351
999
|
|
|
1000
|
+
// src/providers/vectordb/UniversalVectorProvider.ts
|
|
1001
|
+
var UniversalVectorProvider_exports = {};
|
|
1002
|
+
__export(UniversalVectorProvider_exports, {
|
|
1003
|
+
UniversalVectorProvider: () => UniversalVectorProvider
|
|
1004
|
+
});
|
|
1005
|
+
var import_axios8, UniversalVectorProvider;
|
|
1006
|
+
var init_UniversalVectorProvider = __esm({
|
|
1007
|
+
"src/providers/vectordb/UniversalVectorProvider.ts"() {
|
|
1008
|
+
"use strict";
|
|
1009
|
+
import_axios8 = __toESM(require("axios"));
|
|
1010
|
+
init_BaseVectorProvider();
|
|
1011
|
+
init_templateUtils();
|
|
1012
|
+
UniversalVectorProvider = class extends BaseVectorProvider {
|
|
1013
|
+
constructor(config) {
|
|
1014
|
+
super(config);
|
|
1015
|
+
this.opts = config.options;
|
|
1016
|
+
if (!this.opts.baseUrl) {
|
|
1017
|
+
throw new Error("[UniversalVectorProvider] options.baseUrl is required");
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
async initialize() {
|
|
1021
|
+
var _a;
|
|
1022
|
+
this.http = import_axios8.default.create({
|
|
1023
|
+
baseURL: this.opts.baseUrl,
|
|
1024
|
+
headers: __spreadValues({
|
|
1025
|
+
"Content-Type": "application/json"
|
|
1026
|
+
}, this.opts.headers),
|
|
1027
|
+
timeout: (_a = this.opts.timeout) != null ? _a : 3e4
|
|
1028
|
+
});
|
|
1029
|
+
if (!await this.ping()) {
|
|
1030
|
+
throw new Error(`[UniversalVectorProvider] Failed to connect to ${this.opts.baseUrl}`);
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
async upsert(doc, namespace) {
|
|
1034
|
+
var _a, _b, _c;
|
|
1035
|
+
const endpoint = (_a = this.opts.upsertEndpoint) != null ? _a : "/upsert";
|
|
1036
|
+
const template = (_b = this.opts.upsertTemplate) != null ? _b : JSON.stringify({
|
|
1037
|
+
id: "{{id}}",
|
|
1038
|
+
vector: "{{vector}}",
|
|
1039
|
+
content: "{{content}}",
|
|
1040
|
+
namespace: "{{namespace}}",
|
|
1041
|
+
metadata: "{{metadata}}"
|
|
1042
|
+
});
|
|
1043
|
+
const payload = buildPayload(template, {
|
|
1044
|
+
id: doc.id,
|
|
1045
|
+
vector: doc.vector,
|
|
1046
|
+
content: doc.content,
|
|
1047
|
+
namespace: namespace != null ? namespace : this.indexName,
|
|
1048
|
+
metadata: (_c = doc.metadata) != null ? _c : {}
|
|
1049
|
+
});
|
|
1050
|
+
try {
|
|
1051
|
+
await this.http.post(endpoint, payload);
|
|
1052
|
+
} catch (error) {
|
|
1053
|
+
throw new Error(
|
|
1054
|
+
`[UniversalVectorProvider] Upsert failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1055
|
+
);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
async batchUpsert(docs, namespace) {
|
|
1059
|
+
const batchSize = 50;
|
|
1060
|
+
for (let i = 0; i < docs.length; i += batchSize) {
|
|
1061
|
+
const batch = docs.slice(i, i + batchSize);
|
|
1062
|
+
await Promise.all(
|
|
1063
|
+
batch.map((doc) => this.upsert(doc, namespace))
|
|
1064
|
+
);
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
async query(vector, topK, namespace, filter) {
|
|
1068
|
+
var _a, _b;
|
|
1069
|
+
const endpoint = (_a = this.opts.queryEndpoint) != null ? _a : "/query";
|
|
1070
|
+
const template = (_b = this.opts.queryTemplate) != null ? _b : JSON.stringify({
|
|
1071
|
+
vector: "{{vector}}",
|
|
1072
|
+
limit: "{{topK}}",
|
|
1073
|
+
namespace: "{{namespace}}",
|
|
1074
|
+
filter: "{{filter}}"
|
|
1075
|
+
});
|
|
1076
|
+
const payload = buildPayload(template, {
|
|
1077
|
+
vector,
|
|
1078
|
+
topK,
|
|
1079
|
+
namespace: namespace != null ? namespace : this.indexName,
|
|
1080
|
+
filter: filter != null ? filter : {}
|
|
1081
|
+
});
|
|
1082
|
+
try {
|
|
1083
|
+
const response = await this.http.post(endpoint, payload);
|
|
1084
|
+
let results = response.data;
|
|
1085
|
+
if (this.opts.queryResponsePath) {
|
|
1086
|
+
results = resolvePath(response.data, this.opts.queryResponsePath);
|
|
1087
|
+
}
|
|
1088
|
+
if (!Array.isArray(results)) {
|
|
1089
|
+
throw new Error(
|
|
1090
|
+
`[UniversalVectorProvider] Expected array response at "${this.opts.queryResponsePath}", got ${typeof results}`
|
|
1091
|
+
);
|
|
1092
|
+
}
|
|
1093
|
+
return results.map((item) => {
|
|
1094
|
+
var _a2, _b2, _c, _d, _e, _f, _g;
|
|
1095
|
+
return {
|
|
1096
|
+
id: item[(_a2 = this.opts.queryIdField) != null ? _a2 : "id"],
|
|
1097
|
+
score: (_c = item[(_b2 = this.opts.queryScoreField) != null ? _b2 : "score"]) != null ? _c : 0,
|
|
1098
|
+
content: (_e = item[(_d = this.opts.queryContentField) != null ? _d : "content"]) != null ? _e : "",
|
|
1099
|
+
metadata: (_g = item[(_f = this.opts.queryMetadataField) != null ? _f : "metadata"]) != null ? _g : {}
|
|
1100
|
+
};
|
|
1101
|
+
});
|
|
1102
|
+
} catch (error) {
|
|
1103
|
+
throw new Error(
|
|
1104
|
+
`[UniversalVectorProvider] Query failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1105
|
+
);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
async delete(id, namespace) {
|
|
1109
|
+
var _a, _b;
|
|
1110
|
+
const endpoint = (_a = this.opts.deleteEndpoint) != null ? _a : "/delete";
|
|
1111
|
+
const template = (_b = this.opts.deleteTemplate) != null ? _b : JSON.stringify({
|
|
1112
|
+
id: "{{id}}",
|
|
1113
|
+
namespace: "{{namespace}}"
|
|
1114
|
+
});
|
|
1115
|
+
const payload = buildPayload(template, {
|
|
1116
|
+
id,
|
|
1117
|
+
namespace: namespace != null ? namespace : this.indexName
|
|
1118
|
+
});
|
|
1119
|
+
try {
|
|
1120
|
+
await this.http.post(endpoint, payload);
|
|
1121
|
+
} catch (error) {
|
|
1122
|
+
throw new Error(
|
|
1123
|
+
`[UniversalVectorProvider] Delete failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
async deleteNamespace(namespace) {
|
|
1128
|
+
var _a;
|
|
1129
|
+
const endpoint = (_a = this.opts.deleteNamespaceEndpoint) != null ? _a : "/delete-namespace";
|
|
1130
|
+
try {
|
|
1131
|
+
await this.http.post(endpoint, { namespace });
|
|
1132
|
+
} catch (error) {
|
|
1133
|
+
throw new Error(
|
|
1134
|
+
`[UniversalVectorProvider] DeleteNamespace failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1135
|
+
);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
async ping() {
|
|
1139
|
+
var _a;
|
|
1140
|
+
try {
|
|
1141
|
+
const endpoint = (_a = this.opts.pingEndpoint) != null ? _a : "/health";
|
|
1142
|
+
const response = await this.http.get(endpoint, { timeout: 5e3 });
|
|
1143
|
+
return response.status >= 200 && response.status < 300;
|
|
1144
|
+
} catch (e) {
|
|
1145
|
+
return false;
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
async disconnect() {
|
|
1149
|
+
}
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
});
|
|
1153
|
+
|
|
1352
1154
|
// src/handlers/index.ts
|
|
1353
1155
|
var handlers_exports = {};
|
|
1354
1156
|
__export(handlers_exports, {
|
|
@@ -1364,7 +1166,20 @@ var import_server = require("next/server");
|
|
|
1364
1166
|
init_templateUtils();
|
|
1365
1167
|
|
|
1366
1168
|
// src/config/serverConfig.ts
|
|
1367
|
-
var VECTOR_DB_PROVIDERS = [
|
|
1169
|
+
var VECTOR_DB_PROVIDERS = [
|
|
1170
|
+
"pinecone",
|
|
1171
|
+
"pgvector",
|
|
1172
|
+
"postgresql",
|
|
1173
|
+
"mongodb",
|
|
1174
|
+
"milvus",
|
|
1175
|
+
"qdrant",
|
|
1176
|
+
"chromadb",
|
|
1177
|
+
"redis",
|
|
1178
|
+
"weaviate",
|
|
1179
|
+
"rest",
|
|
1180
|
+
"universal_rest",
|
|
1181
|
+
"custom"
|
|
1182
|
+
];
|
|
1368
1183
|
var LLM_PROVIDERS = ["openai", "anthropic", "ollama", "rest", "universal_rest", "custom"];
|
|
1369
1184
|
var EMBEDDING_PROVIDERS = ["openai", "ollama", "rest", "universal_rest", "custom"];
|
|
1370
1185
|
function readString(env, name) {
|
|
@@ -1390,7 +1205,7 @@ function readEnum(env, name, fallback, allowed) {
|
|
|
1390
1205
|
throw new Error(`[getRagConfig] ${name} must be one of: ${allowed.join(", ")}`);
|
|
1391
1206
|
}
|
|
1392
1207
|
function getRagConfig(env = process.env) {
|
|
1393
|
-
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;
|
|
1208
|
+
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;
|
|
1394
1209
|
const projectId = (_b = (_a = readString(env, "RAG_PROJECT_ID")) != null ? _a : readString(env, "NEXT_PUBLIC_PROJECT_ID")) != null ? _b : "__default__";
|
|
1395
1210
|
const vectorProvider = readEnum(env, "VECTOR_DB_PROVIDER", "pinecone", VECTOR_DB_PROVIDERS);
|
|
1396
1211
|
const llmProvider = readEnum(env, "LLM_PROVIDER", "openai", LLM_PROVIDERS);
|
|
@@ -1399,48 +1214,62 @@ function getRagConfig(env = process.env) {
|
|
|
1399
1214
|
const vectorDbOptions = {};
|
|
1400
1215
|
if (vectorProvider === "pinecone") {
|
|
1401
1216
|
vectorDbOptions.apiKey = (_c = readString(env, "PINECONE_API_KEY")) != null ? _c : "";
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
vectorDbOptions.connectionString = (_e = readString(env, "PGVECTOR_CONNECTION_STRING")) != null ? _e : "";
|
|
1217
|
+
} else if (vectorProvider === "pgvector" || vectorProvider === "postgresql") {
|
|
1218
|
+
vectorDbOptions.connectionString = (_d = readString(env, "PGVECTOR_CONNECTION_STRING")) != null ? _d : "";
|
|
1405
1219
|
vectorDbOptions.dimensions = embeddingDimensions;
|
|
1220
|
+
} else if (vectorProvider === "mongodb") {
|
|
1221
|
+
vectorDbOptions.uri = (_e = readString(env, "MONGODB_URI")) != null ? _e : "";
|
|
1222
|
+
vectorDbOptions.database = (_f = readString(env, "MONGODB_DB")) != null ? _f : "";
|
|
1223
|
+
vectorDbOptions.collection = (_g = readString(env, "MONGODB_COLLECTION")) != null ? _g : "";
|
|
1224
|
+
vectorDbOptions.indexName = (_h = readString(env, "MONGODB_INDEX_NAME")) != null ? _h : "vector_index";
|
|
1225
|
+
} else if (vectorProvider === "qdrant") {
|
|
1226
|
+
vectorDbOptions.baseUrl = (_i = readString(env, "QDRANT_URL")) != null ? _i : "http://localhost:6333";
|
|
1227
|
+
vectorDbOptions.apiKey = readString(env, "QDRANT_API_KEY");
|
|
1228
|
+
vectorDbOptions.dimensions = embeddingDimensions;
|
|
1229
|
+
} else if (vectorProvider === "milvus") {
|
|
1230
|
+
vectorDbOptions.baseUrl = (_j = readString(env, "MILVUS_URL")) != null ? _j : "http://localhost:19530";
|
|
1231
|
+
vectorDbOptions.apiKey = readString(env, "MILVUS_API_KEY");
|
|
1232
|
+
} else if (vectorProvider === "chromadb") {
|
|
1233
|
+
vectorDbOptions.baseUrl = (_k = readString(env, "CHROMADB_URL")) != null ? _k : "http://localhost:8000";
|
|
1234
|
+
} else if (vectorProvider === "weaviate") {
|
|
1235
|
+
vectorDbOptions.baseUrl = (_l = readString(env, "WEAVIATE_URL")) != null ? _l : "http://localhost:8080";
|
|
1236
|
+
vectorDbOptions.apiKey = readString(env, "WEAVIATE_API_KEY");
|
|
1237
|
+
} else if (vectorProvider === "redis") {
|
|
1238
|
+
vectorDbOptions.baseUrl = (_m = readString(env, "REDIS_URL")) != null ? _m : "";
|
|
1239
|
+
vectorDbOptions.apiKey = readString(env, "REDIS_API_KEY");
|
|
1406
1240
|
} else if (vectorProvider === "rest") {
|
|
1407
|
-
vectorDbOptions.baseUrl = (
|
|
1241
|
+
vectorDbOptions.baseUrl = (_n = readString(env, "VECTOR_DB_REST_URL")) != null ? _n : "";
|
|
1408
1242
|
vectorDbOptions.headers = readString(env, "VECTOR_DB_REST_API_KEY") ? { "api-key": readString(env, "VECTOR_DB_REST_API_KEY") } : {};
|
|
1409
1243
|
} else if (vectorProvider === "universal_rest") {
|
|
1410
|
-
vectorDbOptions.baseUrl = (
|
|
1244
|
+
vectorDbOptions.baseUrl = (_p = (_o = readString(env, "VECTOR_BASE_URL")) != null ? _o : readString(env, "VECTOR_DB_REST_URL")) != null ? _p : "";
|
|
1411
1245
|
vectorDbOptions.profile = readString(env, "VECTOR_UNIVERSAL_PROFILE");
|
|
1412
1246
|
vectorDbOptions.headers = readString(env, "VECTOR_DB_REST_API_KEY") ? { Authorization: `Bearer ${readString(env, "VECTOR_DB_REST_API_KEY")}` } : {};
|
|
1413
|
-
} else if (vectorProvider === "mongodb") {
|
|
1414
|
-
vectorDbOptions.uri = (_i = readString(env, "MONGODB_URI")) != null ? _i : "";
|
|
1415
|
-
vectorDbOptions.database = (_j = readString(env, "MONGODB_DB")) != null ? _j : "";
|
|
1416
|
-
vectorDbOptions.collection = (_k = readString(env, "MONGODB_COLLECTION")) != null ? _k : "";
|
|
1417
|
-
vectorDbOptions.indexName = (_l = readString(env, "MONGODB_INDEX_NAME")) != null ? _l : "vector_index";
|
|
1418
|
-
} else if (vectorProvider === "qdrant") {
|
|
1419
|
-
vectorDbOptions.baseUrl = (_m = readString(env, "QDRANT_URL")) != null ? _m : "http://localhost:6333";
|
|
1420
|
-
vectorDbOptions.apiKey = readString(env, "QDRANT_API_KEY");
|
|
1421
1247
|
}
|
|
1422
1248
|
const llmApiKeyByProvider = {
|
|
1423
1249
|
openai: readString(env, "OPENAI_API_KEY"),
|
|
1424
1250
|
anthropic: readString(env, "ANTHROPIC_API_KEY"),
|
|
1425
1251
|
ollama: void 0,
|
|
1426
|
-
universal_rest: readString(env, "LLM_API_KEY")
|
|
1252
|
+
universal_rest: readString(env, "LLM_API_KEY"),
|
|
1253
|
+
rest: readString(env, "LLM_API_KEY"),
|
|
1254
|
+
custom: readString(env, "LLM_API_KEY")
|
|
1427
1255
|
};
|
|
1428
1256
|
const embeddingApiKeyByProvider = {
|
|
1429
1257
|
openai: readString(env, "OPENAI_API_KEY"),
|
|
1430
1258
|
ollama: void 0,
|
|
1431
|
-
|
|
1259
|
+
universal_rest: (_q = readString(env, "EMBEDDING_API_KEY")) != null ? _q : readString(env, "OPENAI_API_KEY"),
|
|
1260
|
+
custom: (_r = readString(env, "EMBEDDING_API_KEY")) != null ? _r : readString(env, "OPENAI_API_KEY")
|
|
1432
1261
|
};
|
|
1433
1262
|
return {
|
|
1434
1263
|
projectId,
|
|
1435
1264
|
vectorDb: {
|
|
1436
1265
|
provider: vectorProvider,
|
|
1437
|
-
indexName: (
|
|
1266
|
+
indexName: (_s = readString(env, "VECTOR_DB_INDEX")) != null ? _s : "rag-index",
|
|
1438
1267
|
options: vectorDbOptions
|
|
1439
1268
|
},
|
|
1440
1269
|
llm: {
|
|
1441
1270
|
provider: llmProvider,
|
|
1442
|
-
model: (
|
|
1443
|
-
apiKey: (
|
|
1271
|
+
model: (_t = readString(env, "LLM_MODEL")) != null ? _t : "gpt-4o",
|
|
1272
|
+
apiKey: (_u = llmApiKeyByProvider[llmProvider]) != null ? _u : "",
|
|
1444
1273
|
baseUrl: readString(env, "LLM_BASE_URL"),
|
|
1445
1274
|
systemPrompt: readString(env, "LLM_SYSTEM_PROMPT"),
|
|
1446
1275
|
maxTokens: readNumber(env, "LLM_MAX_TOKENS", 1024),
|
|
@@ -1451,7 +1280,7 @@ function getRagConfig(env = process.env) {
|
|
|
1451
1280
|
},
|
|
1452
1281
|
embedding: {
|
|
1453
1282
|
provider: embeddingProvider,
|
|
1454
|
-
model: (
|
|
1283
|
+
model: (_v = readString(env, "EMBEDDING_MODEL")) != null ? _v : "text-embedding-3-small",
|
|
1455
1284
|
apiKey: embeddingApiKeyByProvider[embeddingProvider],
|
|
1456
1285
|
baseUrl: readString(env, "EMBEDDING_BASE_URL"),
|
|
1457
1286
|
dimensions: embeddingDimensions,
|
|
@@ -1460,16 +1289,16 @@ function getRagConfig(env = process.env) {
|
|
|
1460
1289
|
}
|
|
1461
1290
|
},
|
|
1462
1291
|
ui: {
|
|
1463
|
-
title: (
|
|
1464
|
-
subtitle: (
|
|
1465
|
-
primaryColor: (
|
|
1466
|
-
accentColor: (
|
|
1467
|
-
logoUrl: (
|
|
1468
|
-
placeholder: (
|
|
1469
|
-
showSources: ((
|
|
1470
|
-
welcomeMessage: (
|
|
1471
|
-
visualStyle: (
|
|
1472
|
-
borderRadius: (
|
|
1292
|
+
title: (_x = (_w = readString(env, "NEXT_PUBLIC_UI_TITLE")) != null ? _w : readString(env, "UI_TITLE")) != null ? _x : "AI Assistant",
|
|
1293
|
+
subtitle: (_z = (_y = readString(env, "NEXT_PUBLIC_UI_SUBTITLE")) != null ? _y : readString(env, "UI_SUBTITLE")) != null ? _z : "Powered by RAG",
|
|
1294
|
+
primaryColor: (_B = (_A = readString(env, "NEXT_PUBLIC_PRIMARY_COLOR")) != null ? _A : readString(env, "UI_PRIMARY_COLOR")) != null ? _B : "#10b981",
|
|
1295
|
+
accentColor: (_D = (_C = readString(env, "NEXT_PUBLIC_ACCENT_COLOR")) != null ? _C : readString(env, "UI_ACCENT_COLOR")) != null ? _D : "#3b82f6",
|
|
1296
|
+
logoUrl: (_E = readString(env, "NEXT_PUBLIC_LOGO_URL")) != null ? _E : readString(env, "UI_LOGO_URL"),
|
|
1297
|
+
placeholder: (_G = (_F = readString(env, "NEXT_PUBLIC_PLACEHOLDER")) != null ? _F : readString(env, "UI_PLACEHOLDER")) != null ? _G : "Ask me anything\u2026",
|
|
1298
|
+
showSources: ((_I = (_H = readString(env, "NEXT_PUBLIC_SHOW_SOURCES")) != null ? _H : readString(env, "UI_SHOW_SOURCES")) != null ? _I : "true") !== "false",
|
|
1299
|
+
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.",
|
|
1300
|
+
visualStyle: (_M = (_L = readString(env, "NEXT_PUBLIC_UI_VISUAL_STYLE")) != null ? _L : readString(env, "UI_VISUAL_STYLE")) != null ? _M : "glass",
|
|
1301
|
+
borderRadius: (_O = (_N = readString(env, "NEXT_PUBLIC_UI_BORDER_RADIUS")) != null ? _N : readString(env, "UI_BORDER_RADIUS")) != null ? _O : "xl"
|
|
1473
1302
|
},
|
|
1474
1303
|
rag: {
|
|
1475
1304
|
topK: readNumber(env, "RAG_TOP_K", 5),
|
|
@@ -1545,7 +1374,7 @@ var ConfigValidator = class {
|
|
|
1545
1374
|
errors.push({
|
|
1546
1375
|
field: "embedding",
|
|
1547
1376
|
message: "Embedding config is required when using Anthropic LLM",
|
|
1548
|
-
suggestion: 'Provide embedding config with provider (e.g., "openai")',
|
|
1377
|
+
suggestion: 'Provide embedding config with provider (e.g., "openai" or "ollama")',
|
|
1549
1378
|
severity: "error"
|
|
1550
1379
|
});
|
|
1551
1380
|
}
|
|
@@ -1607,6 +1436,10 @@ var ConfigValidator = class {
|
|
|
1607
1436
|
}
|
|
1608
1437
|
return errors;
|
|
1609
1438
|
}
|
|
1439
|
+
/**
|
|
1440
|
+
* Pinecone — only needs apiKey (environment was removed in SDK v7+).
|
|
1441
|
+
* Options key: { apiKey: string }
|
|
1442
|
+
*/
|
|
1610
1443
|
static validatePineconeConfig(config) {
|
|
1611
1444
|
const errors = [];
|
|
1612
1445
|
const opts = config.options;
|
|
@@ -1618,16 +1451,12 @@ var ConfigValidator = class {
|
|
|
1618
1451
|
severity: "error"
|
|
1619
1452
|
});
|
|
1620
1453
|
}
|
|
1621
|
-
if (!opts.environment) {
|
|
1622
|
-
errors.push({
|
|
1623
|
-
field: "vectorDb.options.environment",
|
|
1624
|
-
message: "Pinecone environment is required",
|
|
1625
|
-
suggestion: 'Set PINECONE_ENVIRONMENT environment variable (e.g., "gcp-starter")',
|
|
1626
|
-
severity: "error"
|
|
1627
|
-
});
|
|
1628
|
-
}
|
|
1629
1454
|
return errors;
|
|
1630
1455
|
}
|
|
1456
|
+
/**
|
|
1457
|
+
* PostgreSQL / pgvector — needs a connection string.
|
|
1458
|
+
* Options key: { connectionString: string }
|
|
1459
|
+
*/
|
|
1631
1460
|
static validatePostgresConfig(config) {
|
|
1632
1461
|
const errors = [];
|
|
1633
1462
|
const opts = config.options;
|
|
@@ -1641,6 +1470,10 @@ var ConfigValidator = class {
|
|
|
1641
1470
|
}
|
|
1642
1471
|
return errors;
|
|
1643
1472
|
}
|
|
1473
|
+
/**
|
|
1474
|
+
* MongoDB — needs uri, database, and collection.
|
|
1475
|
+
* Options keys: { uri: string, database: string, collection: string }
|
|
1476
|
+
*/
|
|
1644
1477
|
static validateMongoDBConfig(config) {
|
|
1645
1478
|
const errors = [];
|
|
1646
1479
|
const opts = config.options;
|
|
@@ -1670,27 +1503,30 @@ var ConfigValidator = class {
|
|
|
1670
1503
|
}
|
|
1671
1504
|
return errors;
|
|
1672
1505
|
}
|
|
1506
|
+
/**
|
|
1507
|
+
* Milvus — accepts baseUrl OR uri (preferred) OR host+port.
|
|
1508
|
+
* MilvusProvider reads opts.baseUrl ?? opts.uri.
|
|
1509
|
+
* Options keys: { baseUrl?: string, uri?: string, host?: string, port?: number }
|
|
1510
|
+
*/
|
|
1673
1511
|
static validateMilvusConfig(config) {
|
|
1674
1512
|
const errors = [];
|
|
1675
1513
|
const opts = config.options;
|
|
1676
|
-
|
|
1514
|
+
const hasBaseUrl = Boolean(opts.baseUrl || opts.uri);
|
|
1515
|
+
const hasHostPort = Boolean(opts.host && opts.port);
|
|
1516
|
+
if (!hasBaseUrl && !hasHostPort) {
|
|
1677
1517
|
errors.push({
|
|
1678
|
-
field: "vectorDb.options.
|
|
1679
|
-
message:
|
|
1680
|
-
suggestion: "Set
|
|
1681
|
-
severity: "error"
|
|
1682
|
-
});
|
|
1683
|
-
}
|
|
1684
|
-
if (!opts.port) {
|
|
1685
|
-
errors.push({
|
|
1686
|
-
field: "vectorDb.options.port",
|
|
1687
|
-
message: "Milvus port is required",
|
|
1688
|
-
suggestion: "Set MILVUS_PORT environment variable (default: 19530)",
|
|
1518
|
+
field: "vectorDb.options.baseUrl",
|
|
1519
|
+
message: 'Milvus connection is required: provide baseUrl (e.g., "http://localhost:19530") or host + port',
|
|
1520
|
+
suggestion: "Set MILVUS_URL environment variable",
|
|
1689
1521
|
severity: "error"
|
|
1690
1522
|
});
|
|
1691
1523
|
}
|
|
1692
1524
|
return errors;
|
|
1693
1525
|
}
|
|
1526
|
+
/**
|
|
1527
|
+
* Qdrant — needs baseUrl (or url alias).
|
|
1528
|
+
* Options keys: { baseUrl: string, apiKey?: string }
|
|
1529
|
+
*/
|
|
1694
1530
|
static validateQdrantConfig(config) {
|
|
1695
1531
|
const errors = [];
|
|
1696
1532
|
const opts = config.options;
|
|
@@ -1704,38 +1540,55 @@ var ConfigValidator = class {
|
|
|
1704
1540
|
}
|
|
1705
1541
|
return errors;
|
|
1706
1542
|
}
|
|
1543
|
+
/**
|
|
1544
|
+
* ChromaDB — accepts baseUrl OR host.
|
|
1545
|
+
* ChromaDBProvider reads opts.baseUrl.
|
|
1546
|
+
* Options keys: { baseUrl?: string, host?: string, port?: number }
|
|
1547
|
+
*/
|
|
1707
1548
|
static validateChromaDBConfig(config) {
|
|
1708
1549
|
const errors = [];
|
|
1709
1550
|
const opts = config.options;
|
|
1710
|
-
if (!opts.
|
|
1551
|
+
if (!opts.baseUrl && !opts.host) {
|
|
1711
1552
|
errors.push({
|
|
1712
|
-
field: "vectorDb.options.
|
|
1713
|
-
message:
|
|
1714
|
-
suggestion: "Set
|
|
1553
|
+
field: "vectorDb.options.baseUrl",
|
|
1554
|
+
message: 'ChromaDB connection is required: provide baseUrl (e.g., "http://localhost:8000") or host',
|
|
1555
|
+
suggestion: "Set CHROMADB_URL environment variable",
|
|
1715
1556
|
severity: "error"
|
|
1716
1557
|
});
|
|
1717
1558
|
}
|
|
1718
1559
|
return errors;
|
|
1719
1560
|
}
|
|
1561
|
+
/**
|
|
1562
|
+
* Redis — accepts baseUrl OR url OR host+port.
|
|
1563
|
+
* RedisProvider reads opts.baseUrl.
|
|
1564
|
+
* Options keys: { baseUrl?: string, url?: string, host?: string, port?: number, apiKey?: string }
|
|
1565
|
+
*/
|
|
1720
1566
|
static validateRedisConfig(config) {
|
|
1721
1567
|
const errors = [];
|
|
1722
1568
|
const opts = config.options;
|
|
1723
|
-
|
|
1569
|
+
const hasUrl = Boolean(opts.baseUrl || opts.url);
|
|
1570
|
+
const hasHostPort = Boolean(opts.host && opts.port);
|
|
1571
|
+
if (!hasUrl && !hasHostPort) {
|
|
1724
1572
|
errors.push({
|
|
1725
|
-
field: "vectorDb.options.
|
|
1726
|
-
message: "Redis connection
|
|
1727
|
-
suggestion: "Set REDIS_URL
|
|
1573
|
+
field: "vectorDb.options.baseUrl",
|
|
1574
|
+
message: "Redis connection is required: provide baseUrl/url or host + port",
|
|
1575
|
+
suggestion: "Set REDIS_URL environment variable",
|
|
1728
1576
|
severity: "error"
|
|
1729
1577
|
});
|
|
1730
1578
|
}
|
|
1731
1579
|
return errors;
|
|
1732
1580
|
}
|
|
1581
|
+
/**
|
|
1582
|
+
* Weaviate — accepts baseUrl OR url.
|
|
1583
|
+
* WeaviateProvider reads opts.baseUrl.
|
|
1584
|
+
* Options keys: { baseUrl?: string, url?: string, apiKey?: string }
|
|
1585
|
+
*/
|
|
1733
1586
|
static validateWeaviateConfig(config) {
|
|
1734
1587
|
const errors = [];
|
|
1735
1588
|
const opts = config.options;
|
|
1736
|
-
if (!opts.url) {
|
|
1589
|
+
if (!opts.baseUrl && !opts.url) {
|
|
1737
1590
|
errors.push({
|
|
1738
|
-
field: "vectorDb.options.
|
|
1591
|
+
field: "vectorDb.options.baseUrl",
|
|
1739
1592
|
message: "Weaviate instance URL is required",
|
|
1740
1593
|
suggestion: "Set WEAVIATE_URL environment variable",
|
|
1741
1594
|
severity: "error"
|
|
@@ -1743,6 +1596,10 @@ var ConfigValidator = class {
|
|
|
1743
1596
|
}
|
|
1744
1597
|
return errors;
|
|
1745
1598
|
}
|
|
1599
|
+
/**
|
|
1600
|
+
* Universal REST / custom REST adapter.
|
|
1601
|
+
* Options key: { baseUrl: string }
|
|
1602
|
+
*/
|
|
1746
1603
|
static validateRestConfig(config) {
|
|
1747
1604
|
const errors = [];
|
|
1748
1605
|
const opts = config.options;
|
|
@@ -1850,7 +1707,7 @@ var ConfigValidator = class {
|
|
|
1850
1707
|
errors.push({
|
|
1851
1708
|
field: "embedding.model",
|
|
1852
1709
|
message: "Embedding model name is required",
|
|
1853
|
-
suggestion: 'e.g., "text-embedding-3-small" for OpenAI',
|
|
1710
|
+
suggestion: 'e.g., "text-embedding-3-small" for OpenAI, "nomic-embed-text" for Ollama',
|
|
1854
1711
|
severity: "error"
|
|
1855
1712
|
});
|
|
1856
1713
|
}
|
|
@@ -1865,7 +1722,7 @@ var ConfigValidator = class {
|
|
|
1865
1722
|
if (config.provider === "ollama" && !config.baseUrl) {
|
|
1866
1723
|
errors.push({
|
|
1867
1724
|
field: "embedding.baseUrl",
|
|
1868
|
-
message: "Ollama base URL is required",
|
|
1725
|
+
message: "Ollama base URL is required for embedding",
|
|
1869
1726
|
suggestion: "Set EMBEDDING_BASE_URL environment variable",
|
|
1870
1727
|
severity: "error"
|
|
1871
1728
|
});
|
|
@@ -1954,9 +1811,11 @@ var ConfigValidator = class {
|
|
|
1954
1811
|
return errors;
|
|
1955
1812
|
}
|
|
1956
1813
|
static isValidCSSColor(color) {
|
|
1957
|
-
const
|
|
1958
|
-
|
|
1959
|
-
|
|
1814
|
+
const hexRegex = /^#([0-9a-f]{3}){1,2}$/i;
|
|
1815
|
+
const rgbRegex = /^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/i;
|
|
1816
|
+
const hslRegex = /^hsla?\((\d+),\s*(\d+)%,\s*(\d+)%(?:,\s*(\d+(?:\.\d+)?))?\)$/i;
|
|
1817
|
+
const namedColors = ["red", "blue", "green", "black", "white", "transparent"];
|
|
1818
|
+
return hexRegex.test(color) || rgbRegex.test(color) || hslRegex.test(color) || namedColors.includes(color.toLowerCase());
|
|
1960
1819
|
}
|
|
1961
1820
|
/**
|
|
1962
1821
|
* Throws if there are error-level validation issues.
|
|
@@ -2040,17 +1899,394 @@ var DocumentChunker = class {
|
|
|
2040
1899
|
}
|
|
2041
1900
|
};
|
|
2042
1901
|
|
|
1902
|
+
// src/llm/providers/OpenAIProvider.ts
|
|
1903
|
+
var import_openai = __toESM(require("openai"));
|
|
1904
|
+
var OpenAIProvider = class {
|
|
1905
|
+
constructor(llmConfig, embeddingConfig) {
|
|
1906
|
+
if (!llmConfig.apiKey) throw new Error("[OpenAIProvider] llmConfig.apiKey is required");
|
|
1907
|
+
this.client = new import_openai.default({ apiKey: llmConfig.apiKey });
|
|
1908
|
+
this.llmConfig = llmConfig;
|
|
1909
|
+
this.embeddingConfig = embeddingConfig;
|
|
1910
|
+
}
|
|
1911
|
+
async chat(messages, context, options) {
|
|
1912
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1913
|
+
const systemContent = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Answer questions based on the provided context.
|
|
1914
|
+
|
|
1915
|
+
Context:
|
|
1916
|
+
${context}`;
|
|
1917
|
+
const systemMessage = {
|
|
1918
|
+
role: "system",
|
|
1919
|
+
content: systemContent.includes("{{context}}") ? systemContent.replace("{{context}}", context) : `${systemContent}
|
|
1920
|
+
|
|
1921
|
+
Context:
|
|
1922
|
+
${context}`
|
|
1923
|
+
};
|
|
1924
|
+
const formattedMessages = [
|
|
1925
|
+
systemMessage,
|
|
1926
|
+
...messages.map((m) => ({
|
|
1927
|
+
role: m.role,
|
|
1928
|
+
content: m.content
|
|
1929
|
+
}))
|
|
1930
|
+
];
|
|
1931
|
+
const completion = await this.client.chat.completions.create({
|
|
1932
|
+
model: this.llmConfig.model,
|
|
1933
|
+
messages: formattedMessages,
|
|
1934
|
+
max_tokens: (_c = (_b = options == null ? void 0 : options.maxTokens) != null ? _b : this.llmConfig.maxTokens) != null ? _c : 1024,
|
|
1935
|
+
temperature: (_e = (_d = options == null ? void 0 : options.temperature) != null ? _d : this.llmConfig.temperature) != null ? _e : 0.7,
|
|
1936
|
+
stop: options == null ? void 0 : options.stop
|
|
1937
|
+
});
|
|
1938
|
+
return (_h = (_g = (_f = completion.choices[0]) == null ? void 0 : _f.message) == null ? void 0 : _g.content) != null ? _h : "";
|
|
1939
|
+
}
|
|
1940
|
+
async embed(text, options) {
|
|
1941
|
+
const results = await this.batchEmbed([text], options);
|
|
1942
|
+
return results[0];
|
|
1943
|
+
}
|
|
1944
|
+
async batchEmbed(texts, options) {
|
|
1945
|
+
var _a, _b, _c, _d, _e;
|
|
1946
|
+
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";
|
|
1947
|
+
const apiKey = (_e = (_d = this.embeddingConfig) == null ? void 0 : _d.apiKey) != null ? _e : this.llmConfig.apiKey;
|
|
1948
|
+
const client = apiKey !== this.llmConfig.apiKey ? new import_openai.default({ apiKey }) : this.client;
|
|
1949
|
+
const response = await client.embeddings.create({ model, input: texts });
|
|
1950
|
+
return response.data.map((d) => d.embedding);
|
|
1951
|
+
}
|
|
1952
|
+
async ping() {
|
|
1953
|
+
try {
|
|
1954
|
+
await this.client.models.list();
|
|
1955
|
+
return true;
|
|
1956
|
+
} catch (err) {
|
|
1957
|
+
console.error("[OpenAIProvider] Ping failed:", err);
|
|
1958
|
+
return false;
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
};
|
|
1962
|
+
|
|
1963
|
+
// src/llm/providers/AnthropicProvider.ts
|
|
1964
|
+
var import_sdk = __toESM(require("@anthropic-ai/sdk"));
|
|
1965
|
+
var AnthropicProvider = class {
|
|
1966
|
+
constructor(llmConfig, embeddingConfig) {
|
|
1967
|
+
if (!llmConfig.apiKey) throw new Error("[AnthropicProvider] llmConfig.apiKey is required");
|
|
1968
|
+
this.client = new import_sdk.default({ apiKey: llmConfig.apiKey });
|
|
1969
|
+
this.llmConfig = llmConfig;
|
|
1970
|
+
this.embeddingConfig = embeddingConfig;
|
|
1971
|
+
}
|
|
1972
|
+
async chat(messages, context, options) {
|
|
1973
|
+
var _a, _b, _c;
|
|
1974
|
+
const systemPrompt = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Use the context below to answer the user's question accurately.
|
|
1975
|
+
|
|
1976
|
+
Context:
|
|
1977
|
+
${context}`;
|
|
1978
|
+
const system = systemPrompt.includes("{{context}}") ? systemPrompt.replace("{{context}}", context) : `${systemPrompt}
|
|
1979
|
+
|
|
1980
|
+
Context:
|
|
1981
|
+
${context}`;
|
|
1982
|
+
const anthropicMessages = messages.map((m) => ({
|
|
1983
|
+
role: m.role === "assistant" ? "assistant" : "user",
|
|
1984
|
+
content: m.content
|
|
1985
|
+
}));
|
|
1986
|
+
const response = await this.client.messages.create({
|
|
1987
|
+
model: this.llmConfig.model,
|
|
1988
|
+
max_tokens: (_c = (_b = options == null ? void 0 : options.maxTokens) != null ? _b : this.llmConfig.maxTokens) != null ? _c : 1024,
|
|
1989
|
+
system,
|
|
1990
|
+
messages: anthropicMessages
|
|
1991
|
+
});
|
|
1992
|
+
const block = response.content[0];
|
|
1993
|
+
return block.type === "text" ? block.text : "";
|
|
1994
|
+
}
|
|
1995
|
+
/**
|
|
1996
|
+
* Anthropic does not offer an embedding API.
|
|
1997
|
+
* This method throws with a clear error so developers know to configure
|
|
1998
|
+
* a separate embedding provider (OpenAI or Ollama).
|
|
1999
|
+
*/
|
|
2000
|
+
async embed(text, options) {
|
|
2001
|
+
void text;
|
|
2002
|
+
void options;
|
|
2003
|
+
throw new Error(
|
|
2004
|
+
'[AnthropicProvider] Anthropic does not provide an embedding API. Set embedding.provider to "openai" or "ollama" in your RagConfig.'
|
|
2005
|
+
);
|
|
2006
|
+
}
|
|
2007
|
+
async batchEmbed(texts, options) {
|
|
2008
|
+
void texts;
|
|
2009
|
+
void options;
|
|
2010
|
+
throw new Error(
|
|
2011
|
+
"[AnthropicProvider] Anthropic does not provide an embedding API."
|
|
2012
|
+
);
|
|
2013
|
+
}
|
|
2014
|
+
async ping() {
|
|
2015
|
+
try {
|
|
2016
|
+
await this.client.models.list();
|
|
2017
|
+
return true;
|
|
2018
|
+
} catch (err) {
|
|
2019
|
+
console.error("[AnthropicProvider] Ping failed:", err);
|
|
2020
|
+
return false;
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
};
|
|
2024
|
+
|
|
2025
|
+
// src/llm/providers/OllamaProvider.ts
|
|
2026
|
+
var import_axios = __toESM(require("axios"));
|
|
2027
|
+
var OllamaProvider = class {
|
|
2028
|
+
constructor(llmConfig, embeddingConfig) {
|
|
2029
|
+
var _a;
|
|
2030
|
+
const baseURL = (_a = llmConfig.baseUrl) != null ? _a : "http://localhost:11434";
|
|
2031
|
+
this.http = import_axios.default.create({ baseURL, timeout: 12e4 });
|
|
2032
|
+
this.llmConfig = llmConfig;
|
|
2033
|
+
this.embeddingConfig = embeddingConfig;
|
|
2034
|
+
}
|
|
2035
|
+
async chat(messages, context, options) {
|
|
2036
|
+
var _a, _b, _c, _d, _e;
|
|
2037
|
+
const systemPrompt = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Use the provided context to answer the user's question.
|
|
2038
|
+
|
|
2039
|
+
Context:
|
|
2040
|
+
${context}`;
|
|
2041
|
+
const system = systemPrompt.includes("{{context}}") ? systemPrompt.replace("{{context}}", context) : `${systemPrompt}
|
|
2042
|
+
|
|
2043
|
+
Context:
|
|
2044
|
+
${context}`;
|
|
2045
|
+
const { data } = await this.http.post("/api/chat", {
|
|
2046
|
+
model: this.llmConfig.model,
|
|
2047
|
+
stream: false,
|
|
2048
|
+
options: {
|
|
2049
|
+
temperature: (_c = (_b = options == null ? void 0 : options.temperature) != null ? _b : this.llmConfig.temperature) != null ? _c : 0.7,
|
|
2050
|
+
num_predict: (_e = (_d = options == null ? void 0 : options.maxTokens) != null ? _d : this.llmConfig.maxTokens) != null ? _e : 1024
|
|
2051
|
+
},
|
|
2052
|
+
messages: [
|
|
2053
|
+
{ role: "system", content: system },
|
|
2054
|
+
...messages.map((m) => ({ role: m.role, content: m.content }))
|
|
2055
|
+
]
|
|
2056
|
+
});
|
|
2057
|
+
return data.message.content;
|
|
2058
|
+
}
|
|
2059
|
+
async embed(text, options) {
|
|
2060
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
2061
|
+
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";
|
|
2062
|
+
const baseURL = (_f = (_e = (_d = this.embeddingConfig) == null ? void 0 : _d.baseUrl) != null ? _e : this.llmConfig.baseUrl) != null ? _f : "http://localhost:11434";
|
|
2063
|
+
const client = baseURL !== ((_g = this.llmConfig.baseUrl) != null ? _g : "http://localhost:11434") ? import_axios.default.create({ baseURL, timeout: 6e4 }) : this.http;
|
|
2064
|
+
const { data } = await client.post("/api/embeddings", {
|
|
2065
|
+
model,
|
|
2066
|
+
prompt: text
|
|
2067
|
+
});
|
|
2068
|
+
return data.embedding;
|
|
2069
|
+
}
|
|
2070
|
+
async batchEmbed(texts, options) {
|
|
2071
|
+
const vectors = [];
|
|
2072
|
+
for (const text of texts) {
|
|
2073
|
+
vectors.push(await this.embed(text, options));
|
|
2074
|
+
}
|
|
2075
|
+
return vectors;
|
|
2076
|
+
}
|
|
2077
|
+
async ping() {
|
|
2078
|
+
try {
|
|
2079
|
+
await this.http.get("/api/tags");
|
|
2080
|
+
return true;
|
|
2081
|
+
} catch (err) {
|
|
2082
|
+
console.error("[OllamaProvider] Ping failed:", err);
|
|
2083
|
+
return false;
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
};
|
|
2087
|
+
|
|
2088
|
+
// src/llm/providers/UniversalLLMAdapter.ts
|
|
2089
|
+
var import_axios2 = __toESM(require("axios"));
|
|
2090
|
+
init_templateUtils();
|
|
2091
|
+
|
|
2092
|
+
// src/config/UniversalProfiles.ts
|
|
2093
|
+
var OPENAI_BASE = {
|
|
2094
|
+
chatPath: "/chat/completions",
|
|
2095
|
+
embedPath: "/embeddings",
|
|
2096
|
+
responseExtractPath: "choices[0].message.content",
|
|
2097
|
+
embedExtractPath: "data[0].embedding",
|
|
2098
|
+
chatPayloadTemplate: '{"model":"{{model}}","messages":{{messages}},"max_tokens":{{maxTokens}},"temperature":{{temperature}}}'
|
|
2099
|
+
};
|
|
2100
|
+
var LLM_PROFILES = {
|
|
2101
|
+
"openai-compatible": OPENAI_BASE,
|
|
2102
|
+
"litellm": __spreadProps(__spreadValues({}, OPENAI_BASE), {
|
|
2103
|
+
chatPayloadTemplate: '{"model":"{{model}}","messages":{{messages}},"max_tokens":{{maxTokens}},"temperature":{{temperature}},"stream":false}'
|
|
2104
|
+
}),
|
|
2105
|
+
"anthropic-claude": {
|
|
2106
|
+
chatPath: "/v1/messages",
|
|
2107
|
+
responseExtractPath: "content[0].text",
|
|
2108
|
+
headers: { "anthropic-version": "2023-06-01" },
|
|
2109
|
+
chatPayloadTemplate: '{"model":"{{model}}","messages":{{messages}},"max_tokens":{{maxTokens}}}'
|
|
2110
|
+
},
|
|
2111
|
+
"google-gemini": {
|
|
2112
|
+
chatPath: "/v1beta/models/{{model}}:generateContent",
|
|
2113
|
+
responseExtractPath: "candidates[0].content.parts[0].text",
|
|
2114
|
+
chatPayloadTemplate: '{"contents":[{"parts":[{"text":"{{messages}}"}]}]}'
|
|
2115
|
+
},
|
|
2116
|
+
"github-copilot": __spreadProps(__spreadValues({}, OPENAI_BASE), {
|
|
2117
|
+
chatPayloadTemplate: '{"model":"{{model}}","messages":{{messages}},"temperature":{{temperature}}}'
|
|
2118
|
+
}),
|
|
2119
|
+
"ollama-standard": {
|
|
2120
|
+
chatPath: "/api/chat",
|
|
2121
|
+
embedPath: "/api/embeddings",
|
|
2122
|
+
responseExtractPath: "message.content",
|
|
2123
|
+
embedExtractPath: "embedding",
|
|
2124
|
+
chatPayloadTemplate: '{"model":"{{model}}","messages":{{messages}},"stream":false}',
|
|
2125
|
+
embedPayloadTemplate: '{"model":"{{model}}","prompt":"{{input}}"}'
|
|
2126
|
+
}
|
|
2127
|
+
};
|
|
2128
|
+
|
|
2129
|
+
// src/llm/providers/UniversalLLMAdapter.ts
|
|
2130
|
+
var UniversalLLMAdapter = class {
|
|
2131
|
+
constructor(config) {
|
|
2132
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
2133
|
+
this.model = config.model;
|
|
2134
|
+
const llmConfig = config;
|
|
2135
|
+
const options = (_a = llmConfig.options) != null ? _a : {};
|
|
2136
|
+
let profile = {};
|
|
2137
|
+
if (typeof options.profile === "string") {
|
|
2138
|
+
profile = LLM_PROFILES[options.profile] || {};
|
|
2139
|
+
} else if (typeof options.profile === "object" && options.profile !== null) {
|
|
2140
|
+
profile = options.profile;
|
|
2141
|
+
}
|
|
2142
|
+
this.opts = __spreadValues(__spreadValues({}, profile), (_b = llmConfig.options) != null ? _b : {});
|
|
2143
|
+
this.systemPrompt = (_c = llmConfig.systemPrompt) != null ? _c : "You are a helpful AI assistant. Use the provided context to answer the user.";
|
|
2144
|
+
this.maxTokens = (_d = llmConfig.maxTokens) != null ? _d : 1024;
|
|
2145
|
+
this.temperature = (_e = llmConfig.temperature) != null ? _e : 0;
|
|
2146
|
+
const baseUrl = (_f = llmConfig.baseUrl) != null ? _f : this.opts.baseUrl;
|
|
2147
|
+
if (!baseUrl) {
|
|
2148
|
+
throw new Error("[UniversalLLMAdapter] baseUrl is required in config or config.options");
|
|
2149
|
+
}
|
|
2150
|
+
this.http = import_axios2.default.create({
|
|
2151
|
+
baseURL: baseUrl,
|
|
2152
|
+
headers: __spreadValues(__spreadValues({
|
|
2153
|
+
"Content-Type": "application/json"
|
|
2154
|
+
}, config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}), this.opts.headers || {}),
|
|
2155
|
+
timeout: (_g = this.opts.timeout) != null ? _g : 6e4
|
|
2156
|
+
});
|
|
2157
|
+
}
|
|
2158
|
+
async chat(messages, context) {
|
|
2159
|
+
var _a, _b;
|
|
2160
|
+
const path = (_a = this.opts.chatPath) != null ? _a : "/chat/completions";
|
|
2161
|
+
const formattedMessages = [
|
|
2162
|
+
{ role: "system", content: `${this.systemPrompt}
|
|
2163
|
+
|
|
2164
|
+
Context:
|
|
2165
|
+
${context != null ? context : "None"}` },
|
|
2166
|
+
...messages
|
|
2167
|
+
];
|
|
2168
|
+
let payload;
|
|
2169
|
+
if (this.opts.chatPayloadTemplate) {
|
|
2170
|
+
payload = buildPayload(this.opts.chatPayloadTemplate, {
|
|
2171
|
+
model: this.model,
|
|
2172
|
+
messages: formattedMessages,
|
|
2173
|
+
maxTokens: this.maxTokens,
|
|
2174
|
+
temperature: this.temperature
|
|
2175
|
+
});
|
|
2176
|
+
} else {
|
|
2177
|
+
payload = {
|
|
2178
|
+
model: this.model,
|
|
2179
|
+
messages: formattedMessages,
|
|
2180
|
+
max_tokens: this.maxTokens,
|
|
2181
|
+
temperature: this.temperature
|
|
2182
|
+
};
|
|
2183
|
+
}
|
|
2184
|
+
const { data } = await this.http.post(path, payload);
|
|
2185
|
+
const extractPath = (_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content";
|
|
2186
|
+
const result = resolvePath(data, extractPath);
|
|
2187
|
+
if (result === void 0) {
|
|
2188
|
+
throw new Error(`[UniversalLLMAdapter] Could not extract text from path '${extractPath}' in response.`);
|
|
2189
|
+
}
|
|
2190
|
+
return String(result);
|
|
2191
|
+
}
|
|
2192
|
+
async embed(text) {
|
|
2193
|
+
var _a, _b;
|
|
2194
|
+
const path = (_a = this.opts.embedPath) != null ? _a : "/embeddings";
|
|
2195
|
+
let payload;
|
|
2196
|
+
if (this.opts.embedPayloadTemplate) {
|
|
2197
|
+
payload = buildPayload(this.opts.embedPayloadTemplate, {
|
|
2198
|
+
model: this.model,
|
|
2199
|
+
input: text
|
|
2200
|
+
});
|
|
2201
|
+
} else {
|
|
2202
|
+
payload = {
|
|
2203
|
+
model: this.model,
|
|
2204
|
+
input: text
|
|
2205
|
+
};
|
|
2206
|
+
}
|
|
2207
|
+
const { data } = await this.http.post(path, payload);
|
|
2208
|
+
const extractPath = (_b = this.opts.embedExtractPath) != null ? _b : "data[0].embedding";
|
|
2209
|
+
const vector = resolvePath(data, extractPath);
|
|
2210
|
+
if (!Array.isArray(vector)) {
|
|
2211
|
+
throw new Error(`[UniversalLLMAdapter] Expected a number array at '${extractPath}' for embeddings.`);
|
|
2212
|
+
}
|
|
2213
|
+
return vector;
|
|
2214
|
+
}
|
|
2215
|
+
async batchEmbed(texts) {
|
|
2216
|
+
const vectors = [];
|
|
2217
|
+
for (const text of texts) {
|
|
2218
|
+
vectors.push(await this.embed(text));
|
|
2219
|
+
}
|
|
2220
|
+
return vectors;
|
|
2221
|
+
}
|
|
2222
|
+
async ping() {
|
|
2223
|
+
try {
|
|
2224
|
+
if (this.opts.pingPath) {
|
|
2225
|
+
await this.http.get(this.opts.pingPath);
|
|
2226
|
+
}
|
|
2227
|
+
return true;
|
|
2228
|
+
} catch (err) {
|
|
2229
|
+
console.error("[UniversalLLMAdapter] Ping failed:", err);
|
|
2230
|
+
return false;
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
};
|
|
2234
|
+
|
|
2235
|
+
// src/llm/LLMFactory.ts
|
|
2236
|
+
var LLMFactory = class _LLMFactory {
|
|
2237
|
+
static create(llmConfig, embeddingConfig) {
|
|
2238
|
+
var _a;
|
|
2239
|
+
switch (llmConfig.provider) {
|
|
2240
|
+
case "openai":
|
|
2241
|
+
return new OpenAIProvider(llmConfig, embeddingConfig);
|
|
2242
|
+
case "anthropic":
|
|
2243
|
+
return new AnthropicProvider(llmConfig, embeddingConfig);
|
|
2244
|
+
case "ollama":
|
|
2245
|
+
return new OllamaProvider(llmConfig, embeddingConfig);
|
|
2246
|
+
case "rest":
|
|
2247
|
+
case "universal_rest":
|
|
2248
|
+
case "custom":
|
|
2249
|
+
return new UniversalLLMAdapter(llmConfig);
|
|
2250
|
+
default:
|
|
2251
|
+
if (llmConfig.baseUrl || ((_a = llmConfig.options) == null ? void 0 : _a.baseUrl)) {
|
|
2252
|
+
return new UniversalLLMAdapter(llmConfig);
|
|
2253
|
+
}
|
|
2254
|
+
throw new Error(
|
|
2255
|
+
`[LLMFactory] Unknown provider "${llmConfig.provider}". Supported: openai | anthropic | ollama | rest | custom`
|
|
2256
|
+
);
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2259
|
+
/**
|
|
2260
|
+
* Creates a dedicated embedding-only provider.
|
|
2261
|
+
* Useful when the LLM provider (e.g. Anthropic) doesn't support embeddings.
|
|
2262
|
+
*/
|
|
2263
|
+
static createEmbeddingProvider(embeddingConfig) {
|
|
2264
|
+
const fakeLLMConfig = {
|
|
2265
|
+
provider: embeddingConfig.provider,
|
|
2266
|
+
model: embeddingConfig.model,
|
|
2267
|
+
apiKey: embeddingConfig.apiKey,
|
|
2268
|
+
baseUrl: embeddingConfig.baseUrl,
|
|
2269
|
+
options: embeddingConfig.options
|
|
2270
|
+
};
|
|
2271
|
+
return _LLMFactory.create(fakeLLMConfig, embeddingConfig);
|
|
2272
|
+
}
|
|
2273
|
+
};
|
|
2274
|
+
|
|
2043
2275
|
// src/core/ProviderRegistry.ts
|
|
2044
|
-
init_LLMFactory();
|
|
2045
2276
|
var ProviderRegistry = class {
|
|
2046
2277
|
/**
|
|
2047
|
-
* Register a custom vector provider.
|
|
2278
|
+
* Register a custom vector provider class by name.
|
|
2279
|
+
* The name must match the provider value used in VectorDBConfig.provider.
|
|
2280
|
+
*
|
|
2281
|
+
* @example
|
|
2282
|
+
* ProviderRegistry.registerVectorProvider('my-db', MyCustomProvider);
|
|
2048
2283
|
*/
|
|
2049
2284
|
static registerVectorProvider(name, providerClass) {
|
|
2050
2285
|
this.vectorProviders[name] = providerClass;
|
|
2051
2286
|
}
|
|
2052
2287
|
/**
|
|
2053
2288
|
* Creates a vector database provider based on the configuration.
|
|
2289
|
+
* Built-in providers are dynamically imported to avoid bundling all SDKs.
|
|
2054
2290
|
*/
|
|
2055
2291
|
static async createVectorProvider(config) {
|
|
2056
2292
|
const { provider } = config;
|
|
@@ -2058,33 +2294,48 @@ var ProviderRegistry = class {
|
|
|
2058
2294
|
return new this.vectorProviders[provider](config);
|
|
2059
2295
|
}
|
|
2060
2296
|
switch (provider) {
|
|
2061
|
-
case "pinecone":
|
|
2297
|
+
case "pinecone": {
|
|
2062
2298
|
const { PineconeProvider: PineconeProvider2 } = await Promise.resolve().then(() => (init_PineconeProvider(), PineconeProvider_exports));
|
|
2063
2299
|
return new PineconeProvider2(config);
|
|
2300
|
+
}
|
|
2064
2301
|
case "pgvector":
|
|
2065
|
-
case "postgresql":
|
|
2302
|
+
case "postgresql": {
|
|
2066
2303
|
const { PostgreSQLProvider: PostgreSQLProvider2 } = await Promise.resolve().then(() => (init_PostgreSQLProvider(), PostgreSQLProvider_exports));
|
|
2067
2304
|
return new PostgreSQLProvider2(config);
|
|
2068
|
-
|
|
2305
|
+
}
|
|
2306
|
+
case "mongodb": {
|
|
2069
2307
|
const { MongoDBProvider: MongoDBProvider2 } = await Promise.resolve().then(() => (init_MongoDBProvider(), MongoDBProvider_exports));
|
|
2070
2308
|
return new MongoDBProvider2(config);
|
|
2071
|
-
|
|
2309
|
+
}
|
|
2310
|
+
case "milvus": {
|
|
2072
2311
|
const { MilvusProvider: MilvusProvider2 } = await Promise.resolve().then(() => (init_MilvusProvider(), MilvusProvider_exports));
|
|
2073
2312
|
return new MilvusProvider2(config);
|
|
2074
|
-
|
|
2313
|
+
}
|
|
2314
|
+
case "qdrant": {
|
|
2075
2315
|
const { QdrantProvider: QdrantProvider2 } = await Promise.resolve().then(() => (init_QdrantProvider(), QdrantProvider_exports));
|
|
2076
2316
|
return new QdrantProvider2(config);
|
|
2077
|
-
|
|
2317
|
+
}
|
|
2318
|
+
case "chromadb": {
|
|
2078
2319
|
const { ChromaDBProvider: ChromaDBProvider2 } = await Promise.resolve().then(() => (init_ChromaDBProvider(), ChromaDBProvider_exports));
|
|
2079
2320
|
return new ChromaDBProvider2(config);
|
|
2080
|
-
|
|
2321
|
+
}
|
|
2322
|
+
case "redis": {
|
|
2081
2323
|
const { RedisProvider: RedisProvider2 } = await Promise.resolve().then(() => (init_RedisProvider(), RedisProvider_exports));
|
|
2082
2324
|
return new RedisProvider2(config);
|
|
2083
|
-
|
|
2325
|
+
}
|
|
2326
|
+
case "weaviate": {
|
|
2084
2327
|
const { WeaviateProvider: WeaviateProvider2 } = await Promise.resolve().then(() => (init_WeaviateProvider(), WeaviateProvider_exports));
|
|
2085
2328
|
return new WeaviateProvider2(config);
|
|
2329
|
+
}
|
|
2330
|
+
case "universal_rest":
|
|
2331
|
+
case "rest": {
|
|
2332
|
+
const { UniversalVectorProvider: UniversalVectorProvider2 } = await Promise.resolve().then(() => (init_UniversalVectorProvider(), UniversalVectorProvider_exports));
|
|
2333
|
+
return new UniversalVectorProvider2(config);
|
|
2334
|
+
}
|
|
2086
2335
|
default:
|
|
2087
|
-
throw new Error(
|
|
2336
|
+
throw new Error(
|
|
2337
|
+
`[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).`
|
|
2338
|
+
);
|
|
2088
2339
|
}
|
|
2089
2340
|
}
|
|
2090
2341
|
/**
|
|
@@ -2320,6 +2571,83 @@ ${errorMessages}`
|
|
|
2320
2571
|
}
|
|
2321
2572
|
};
|
|
2322
2573
|
|
|
2574
|
+
// src/config/EmbeddingStrategy.ts
|
|
2575
|
+
var EmbeddingStrategyResolver = class {
|
|
2576
|
+
/**
|
|
2577
|
+
* Determine strategy based on LLM and embedding configs
|
|
2578
|
+
*/
|
|
2579
|
+
static determineStrategy(llmConfig, embeddingConfig) {
|
|
2580
|
+
if (!embeddingConfig) {
|
|
2581
|
+
return this.supportsEmbedding(llmConfig.provider) ? "integrated" /* INTEGRATED */ : "separate" /* SEPARATE */;
|
|
2582
|
+
}
|
|
2583
|
+
if (embeddingConfig.provider !== llmConfig.provider) {
|
|
2584
|
+
return "separate" /* SEPARATE */;
|
|
2585
|
+
}
|
|
2586
|
+
if (embeddingConfig.model !== llmConfig.model) {
|
|
2587
|
+
return "external" /* EXTERNAL */;
|
|
2588
|
+
}
|
|
2589
|
+
return "integrated" /* INTEGRATED */;
|
|
2590
|
+
}
|
|
2591
|
+
/**
|
|
2592
|
+
* Resolve and initialize providers according to the strategy
|
|
2593
|
+
*/
|
|
2594
|
+
static async resolve(llmConfig, embeddingConfig) {
|
|
2595
|
+
const strategy = this.determineStrategy(llmConfig, embeddingConfig);
|
|
2596
|
+
const llmProvider = LLMFactory.create(llmConfig, embeddingConfig);
|
|
2597
|
+
let embeddingProvider;
|
|
2598
|
+
switch (strategy) {
|
|
2599
|
+
case "integrated" /* INTEGRATED */:
|
|
2600
|
+
embeddingProvider = llmProvider;
|
|
2601
|
+
break;
|
|
2602
|
+
case "separate" /* SEPARATE */:
|
|
2603
|
+
if (!embeddingConfig) {
|
|
2604
|
+
throw new Error(
|
|
2605
|
+
"[EmbeddingStrategyResolver] SEPARATE strategy requires embeddingConfig"
|
|
2606
|
+
);
|
|
2607
|
+
}
|
|
2608
|
+
embeddingProvider = LLMFactory.createEmbeddingProvider(embeddingConfig);
|
|
2609
|
+
break;
|
|
2610
|
+
case "external" /* EXTERNAL */:
|
|
2611
|
+
if (!embeddingConfig) {
|
|
2612
|
+
throw new Error(
|
|
2613
|
+
"[EmbeddingStrategyResolver] EXTERNAL strategy requires embeddingConfig"
|
|
2614
|
+
);
|
|
2615
|
+
}
|
|
2616
|
+
embeddingProvider = LLMFactory.createEmbeddingProvider(embeddingConfig);
|
|
2617
|
+
break;
|
|
2618
|
+
default:
|
|
2619
|
+
throw new Error(`[EmbeddingStrategyResolver] Unknown strategy: ${strategy}`);
|
|
2620
|
+
}
|
|
2621
|
+
return {
|
|
2622
|
+
strategy,
|
|
2623
|
+
embeddingProvider,
|
|
2624
|
+
llmProvider
|
|
2625
|
+
};
|
|
2626
|
+
}
|
|
2627
|
+
/**
|
|
2628
|
+
* Check if an LLM provider natively supports embeddings
|
|
2629
|
+
*/
|
|
2630
|
+
static supportsEmbedding(provider) {
|
|
2631
|
+
const providersWithEmbedding = ["openai", "ollama", "rest", "universal_rest"];
|
|
2632
|
+
return providersWithEmbedding.includes(provider);
|
|
2633
|
+
}
|
|
2634
|
+
/**
|
|
2635
|
+
* Get a human-readable description of the strategy
|
|
2636
|
+
*/
|
|
2637
|
+
static getDescription(strategy) {
|
|
2638
|
+
switch (strategy) {
|
|
2639
|
+
case "integrated" /* INTEGRATED */:
|
|
2640
|
+
return "Using LLM provider for both chat and embeddings";
|
|
2641
|
+
case "separate" /* SEPARATE */:
|
|
2642
|
+
return "Using separate embedding provider";
|
|
2643
|
+
case "external" /* EXTERNAL */:
|
|
2644
|
+
return "Using external embedding service";
|
|
2645
|
+
default:
|
|
2646
|
+
return "Unknown strategy";
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
};
|
|
2650
|
+
|
|
2323
2651
|
// src/core/Pipeline.ts
|
|
2324
2652
|
var Pipeline = class {
|
|
2325
2653
|
constructor(config) {
|
|
@@ -2334,18 +2662,17 @@ var Pipeline = class {
|
|
|
2334
2662
|
async initialize() {
|
|
2335
2663
|
if (this.initialised) return;
|
|
2336
2664
|
this.vectorDB = await ProviderRegistry.createVectorProvider(this.config.vectorDb);
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
}
|
|
2665
|
+
const { llmProvider, embeddingProvider } = await EmbeddingStrategyResolver.resolve(
|
|
2666
|
+
this.config.llm,
|
|
2667
|
+
this.config.embedding
|
|
2668
|
+
);
|
|
2669
|
+
this.llmProvider = llmProvider;
|
|
2670
|
+
this.embeddingProvider = embeddingProvider;
|
|
2344
2671
|
await this.vectorDB.initialize();
|
|
2345
2672
|
this.initialised = true;
|
|
2346
2673
|
}
|
|
2347
2674
|
/**
|
|
2348
|
-
* Ingest documents with automatic chunking, embedding, and batch
|
|
2675
|
+
* Ingest documents with automatic chunking, embedding, and batch upsert.
|
|
2349
2676
|
* Handles retries for transient failures.
|
|
2350
2677
|
*/
|
|
2351
2678
|
async ingest(documents, namespace) {
|
|
@@ -2358,19 +2685,18 @@ var Pipeline = class {
|
|
|
2358
2685
|
docId: doc.docId,
|
|
2359
2686
|
metadata: doc.metadata
|
|
2360
2687
|
});
|
|
2361
|
-
const
|
|
2688
|
+
const embedBatchOptions = {
|
|
2362
2689
|
batchSize: 50,
|
|
2363
|
-
// Embedding batch size
|
|
2364
2690
|
maxRetries: 3,
|
|
2365
2691
|
initialDelayMs: 100
|
|
2366
2692
|
};
|
|
2367
2693
|
const vectors = await BatchProcessor.mapWithRetry(
|
|
2368
2694
|
chunks.map((c) => c.content),
|
|
2369
2695
|
(text) => this.embeddingProvider.embed(text),
|
|
2370
|
-
|
|
2696
|
+
embedBatchOptions
|
|
2371
2697
|
);
|
|
2372
2698
|
if (vectors.length !== chunks.length) {
|
|
2373
|
-
throw new Error(`Embedding
|
|
2699
|
+
throw new Error(`Embedding mismatch: got ${vectors.length} vectors for ${chunks.length} chunks`);
|
|
2374
2700
|
}
|
|
2375
2701
|
const upsertDocs = chunks.map((chunk, i) => ({
|
|
2376
2702
|
id: chunk.id,
|
|
@@ -2397,10 +2723,7 @@ var Pipeline = class {
|
|
|
2397
2723
|
});
|
|
2398
2724
|
} catch (error) {
|
|
2399
2725
|
console.error(`[Pipeline] Failed to ingest document ${doc.docId}:`, error);
|
|
2400
|
-
results.push({
|
|
2401
|
-
docId: doc.docId,
|
|
2402
|
-
chunksIngested: 0
|
|
2403
|
-
});
|
|
2726
|
+
results.push({ docId: doc.docId, chunksIngested: 0 });
|
|
2404
2727
|
}
|
|
2405
2728
|
}
|
|
2406
2729
|
return results;
|
|
@@ -2430,7 +2753,6 @@ ${m.content}`).join("\n\n---\n\n") : "No relevant context found.";
|
|
|
2430
2753
|
var ProviderHealthCheck = class {
|
|
2431
2754
|
/**
|
|
2432
2755
|
* Validates vector database configuration before initialization.
|
|
2433
|
-
* Performs connectivity checks and verifies required resources exist.
|
|
2434
2756
|
*/
|
|
2435
2757
|
static async checkVectorProvider(config) {
|
|
2436
2758
|
const timestamp = Date.now();
|
|
@@ -2438,7 +2760,6 @@ var ProviderHealthCheck = class {
|
|
|
2438
2760
|
projectId: "health-check",
|
|
2439
2761
|
vectorDb: config,
|
|
2440
2762
|
llm: { provider: "openai", model: "gpt-4o" },
|
|
2441
|
-
// dummy
|
|
2442
2763
|
embedding: { provider: "openai", model: "text-embedding-3-small" }
|
|
2443
2764
|
});
|
|
2444
2765
|
const vectorDbErrors = configErrors.filter((e) => e.field.startsWith("vectorDb"));
|
|
@@ -2508,10 +2829,7 @@ var ProviderHealthCheck = class {
|
|
|
2508
2829
|
return {
|
|
2509
2830
|
healthy: true,
|
|
2510
2831
|
provider: "pinecone",
|
|
2511
|
-
capabilities: {
|
|
2512
|
-
indexes: indexNames.length,
|
|
2513
|
-
targetIndex: config.indexName
|
|
2514
|
-
},
|
|
2832
|
+
capabilities: { indexes: indexNames.length, targetIndex: config.indexName },
|
|
2515
2833
|
timestamp
|
|
2516
2834
|
};
|
|
2517
2835
|
} catch (error) {
|
|
@@ -2530,9 +2848,7 @@ var ProviderHealthCheck = class {
|
|
|
2530
2848
|
const client = new Client({ connectionString: opts.connectionString });
|
|
2531
2849
|
await client.connect();
|
|
2532
2850
|
const result = await client.query(`
|
|
2533
|
-
SELECT EXISTS(
|
|
2534
|
-
SELECT 1 FROM pg_extension WHERE extname = 'vector'
|
|
2535
|
-
);
|
|
2851
|
+
SELECT EXISTS(SELECT 1 FROM pg_extension WHERE extname = 'vector');
|
|
2536
2852
|
`);
|
|
2537
2853
|
const hasVector = result.rows[0].exists;
|
|
2538
2854
|
await client.end();
|
|
@@ -2567,7 +2883,7 @@ var ProviderHealthCheck = class {
|
|
|
2567
2883
|
provider: "mongodb",
|
|
2568
2884
|
capabilities: {
|
|
2569
2885
|
collections: collectionNames.length,
|
|
2570
|
-
targetCollection: hasCollection ? opts.collection : "NOT FOUND"
|
|
2886
|
+
targetCollection: hasCollection ? opts.collection : "NOT FOUND (will be created on first insert)"
|
|
2571
2887
|
},
|
|
2572
2888
|
timestamp
|
|
2573
2889
|
};
|
|
@@ -2580,16 +2896,16 @@ var ProviderHealthCheck = class {
|
|
|
2580
2896
|
};
|
|
2581
2897
|
}
|
|
2582
2898
|
}
|
|
2899
|
+
/**
|
|
2900
|
+
* Milvus health check — uses baseUrl/uri (matching MilvusProvider constructor).
|
|
2901
|
+
*/
|
|
2583
2902
|
static async checkMilvus(config, timestamp) {
|
|
2584
2903
|
const opts = config.options;
|
|
2585
|
-
const
|
|
2586
|
-
const port = opts.port || 19530;
|
|
2904
|
+
const baseUrl = opts.baseUrl || opts.uri || (opts.host ? `http://${opts.host}:${opts.port || 19530}` : "http://localhost:19530");
|
|
2587
2905
|
try {
|
|
2588
2906
|
const controller = new AbortController();
|
|
2589
2907
|
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
2590
|
-
const response = await fetch(
|
|
2591
|
-
signal: controller.signal
|
|
2592
|
-
});
|
|
2908
|
+
const response = await fetch(`${baseUrl}/v1/health`, { signal: controller.signal });
|
|
2593
2909
|
clearTimeout(timeoutId);
|
|
2594
2910
|
if (!response.ok) {
|
|
2595
2911
|
throw new Error(`Health check returned ${response.status}`);
|
|
@@ -2597,7 +2913,7 @@ var ProviderHealthCheck = class {
|
|
|
2597
2913
|
return {
|
|
2598
2914
|
healthy: true,
|
|
2599
2915
|
provider: "milvus",
|
|
2600
|
-
capabilities: { endpoint:
|
|
2916
|
+
capabilities: { endpoint: baseUrl },
|
|
2601
2917
|
timestamp
|
|
2602
2918
|
};
|
|
2603
2919
|
} catch (error) {
|
|
@@ -2609,14 +2925,18 @@ var ProviderHealthCheck = class {
|
|
|
2609
2925
|
};
|
|
2610
2926
|
}
|
|
2611
2927
|
}
|
|
2928
|
+
/**
|
|
2929
|
+
* Qdrant health check — uses baseUrl (matching QdrantProvider constructor).
|
|
2930
|
+
*/
|
|
2612
2931
|
static async checkQdrant(config, timestamp) {
|
|
2613
2932
|
const opts = config.options;
|
|
2614
|
-
const baseUrl = opts.baseUrl || opts.url || "http://localhost:6333";
|
|
2933
|
+
const baseUrl = (opts.baseUrl || opts.url || "http://localhost:6333").replace(/\/$/, "");
|
|
2615
2934
|
try {
|
|
2616
|
-
const
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
}
|
|
2935
|
+
const apiKey = opts.apiKey;
|
|
2936
|
+
const response = await fetch(`${baseUrl}/`, {
|
|
2937
|
+
headers: apiKey ? { "api-key": apiKey } : {}
|
|
2938
|
+
});
|
|
2939
|
+
if (!response.ok) throw new Error(`Health check returned ${response.status}`);
|
|
2620
2940
|
const health = await response.json();
|
|
2621
2941
|
return {
|
|
2622
2942
|
healthy: true,
|
|
@@ -2633,19 +2953,19 @@ var ProviderHealthCheck = class {
|
|
|
2633
2953
|
};
|
|
2634
2954
|
}
|
|
2635
2955
|
}
|
|
2956
|
+
/**
|
|
2957
|
+
* ChromaDB health check — uses baseUrl/host (matching ChromaDBProvider constructor).
|
|
2958
|
+
*/
|
|
2636
2959
|
static async checkChromaDB(config, timestamp) {
|
|
2637
2960
|
const opts = config.options;
|
|
2638
|
-
const
|
|
2639
|
-
const port = opts.port || 8e3;
|
|
2961
|
+
const baseUrl = opts.baseUrl || (opts.host ? `http://${opts.host}:${opts.port || 8e3}` : "http://localhost:8000");
|
|
2640
2962
|
try {
|
|
2641
|
-
const response = await fetch(
|
|
2642
|
-
if (!response.ok) {
|
|
2643
|
-
throw new Error(`Health check returned ${response.status}`);
|
|
2644
|
-
}
|
|
2963
|
+
const response = await fetch(`${baseUrl}/api/v1/heartbeat`);
|
|
2964
|
+
if (!response.ok) throw new Error(`Health check returned ${response.status}`);
|
|
2645
2965
|
return {
|
|
2646
2966
|
healthy: true,
|
|
2647
2967
|
provider: "chromadb",
|
|
2648
|
-
capabilities: { endpoint:
|
|
2968
|
+
capabilities: { endpoint: baseUrl },
|
|
2649
2969
|
timestamp
|
|
2650
2970
|
};
|
|
2651
2971
|
} catch (error) {
|
|
@@ -2657,38 +2977,43 @@ var ProviderHealthCheck = class {
|
|
|
2657
2977
|
};
|
|
2658
2978
|
}
|
|
2659
2979
|
}
|
|
2980
|
+
/**
|
|
2981
|
+
* Redis health check — Redis is TCP-only (no HTTP endpoint).
|
|
2982
|
+
* We report healthy=true with a note; actual connectivity is validated at first operation.
|
|
2983
|
+
*/
|
|
2660
2984
|
static async checkRedis(config, timestamp) {
|
|
2661
2985
|
const opts = config.options;
|
|
2662
|
-
const url = opts.url || `redis://${opts.host}:${opts.port}
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
healthy: true,
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
timestamp
|
|
2670
|
-
};
|
|
2671
|
-
} catch (error) {
|
|
2672
|
-
return {
|
|
2673
|
-
healthy: false,
|
|
2674
|
-
provider: "redis",
|
|
2675
|
-
error: `Connection check failed: ${error instanceof Error ? error.message : String(error)}. Ensure Redis is running at ${url}`,
|
|
2676
|
-
timestamp
|
|
2677
|
-
};
|
|
2986
|
+
const url = opts.baseUrl || opts.url || (opts.host && opts.port ? `redis://${opts.host}:${opts.port}` : "redis://localhost:6379");
|
|
2987
|
+
if (url.startsWith("http")) {
|
|
2988
|
+
try {
|
|
2989
|
+
await fetch(url);
|
|
2990
|
+
return { healthy: true, provider: "redis", capabilities: { endpoint: url }, timestamp };
|
|
2991
|
+
} catch (e) {
|
|
2992
|
+
}
|
|
2678
2993
|
}
|
|
2994
|
+
return {
|
|
2995
|
+
healthy: true,
|
|
2996
|
+
provider: "redis",
|
|
2997
|
+
capabilities: {
|
|
2998
|
+
endpoint: url,
|
|
2999
|
+
note: "Redis uses TCP; connectivity is validated on first operation."
|
|
3000
|
+
},
|
|
3001
|
+
timestamp
|
|
3002
|
+
};
|
|
2679
3003
|
}
|
|
3004
|
+
/**
|
|
3005
|
+
* Weaviate health check — uses baseUrl/url (matching WeaviateProvider constructor).
|
|
3006
|
+
*/
|
|
2680
3007
|
static async checkWeaviate(config, timestamp) {
|
|
2681
3008
|
const opts = config.options;
|
|
2682
|
-
const
|
|
3009
|
+
const baseUrl = (opts.baseUrl || opts.url || "http://localhost:8080").replace(/\/$/, "");
|
|
2683
3010
|
try {
|
|
2684
|
-
const response = await fetch(`${
|
|
2685
|
-
if (!response.ok) {
|
|
2686
|
-
throw new Error(`Health check returned ${response.status}`);
|
|
2687
|
-
}
|
|
3011
|
+
const response = await fetch(`${baseUrl}/v1/.well-known/ready`);
|
|
3012
|
+
if (!response.ok) throw new Error(`Health check returned ${response.status}`);
|
|
2688
3013
|
return {
|
|
2689
3014
|
healthy: true,
|
|
2690
3015
|
provider: "weaviate",
|
|
2691
|
-
capabilities: { endpoint:
|
|
3016
|
+
capabilities: { endpoint: baseUrl },
|
|
2692
3017
|
timestamp
|
|
2693
3018
|
};
|
|
2694
3019
|
} catch (error) {
|
|
@@ -2704,12 +3029,7 @@ var ProviderHealthCheck = class {
|
|
|
2704
3029
|
const opts = config.options;
|
|
2705
3030
|
const baseUrl = (opts.baseUrl || "").replace(/\/$/, "");
|
|
2706
3031
|
if (!baseUrl) {
|
|
2707
|
-
return {
|
|
2708
|
-
healthy: false,
|
|
2709
|
-
provider: "rest",
|
|
2710
|
-
error: "baseUrl is required",
|
|
2711
|
-
timestamp
|
|
2712
|
-
};
|
|
3032
|
+
return { healthy: false, provider: "rest", error: "baseUrl is required", timestamp };
|
|
2713
3033
|
}
|
|
2714
3034
|
try {
|
|
2715
3035
|
const response = await fetch(`${baseUrl}/health`, {
|
|
@@ -2772,11 +3092,7 @@ var ProviderHealthCheck = class {
|
|
|
2772
3092
|
return {
|
|
2773
3093
|
healthy: true,
|
|
2774
3094
|
provider: "openai",
|
|
2775
|
-
capabilities: {
|
|
2776
|
-
model: config.model,
|
|
2777
|
-
available: hasModel,
|
|
2778
|
-
totalModels: models.data.length
|
|
2779
|
-
},
|
|
3095
|
+
capabilities: { model: config.model, available: hasModel, totalModels: models.data.length },
|
|
2780
3096
|
timestamp
|
|
2781
3097
|
};
|
|
2782
3098
|
} catch (error) {
|
|
@@ -2797,12 +3113,7 @@ var ProviderHealthCheck = class {
|
|
|
2797
3113
|
max_tokens: 10,
|
|
2798
3114
|
messages: [{ role: "user", content: "ping" }]
|
|
2799
3115
|
});
|
|
2800
|
-
return {
|
|
2801
|
-
healthy: true,
|
|
2802
|
-
provider: "anthropic",
|
|
2803
|
-
capabilities: { model: config.model },
|
|
2804
|
-
timestamp
|
|
2805
|
-
};
|
|
3116
|
+
return { healthy: true, provider: "anthropic", capabilities: { model: config.model }, timestamp };
|
|
2806
3117
|
} catch (error) {
|
|
2807
3118
|
return {
|
|
2808
3119
|
healthy: false,
|
|
@@ -2816,20 +3127,14 @@ var ProviderHealthCheck = class {
|
|
|
2816
3127
|
const baseUrl = (config.baseUrl || "http://localhost:11434").replace(/\/$/, "");
|
|
2817
3128
|
try {
|
|
2818
3129
|
const response = await fetch(`${baseUrl}/api/tags`);
|
|
2819
|
-
if (!response.ok) {
|
|
2820
|
-
throw new Error(`Health check returned ${response.status}`);
|
|
2821
|
-
}
|
|
3130
|
+
if (!response.ok) throw new Error(`Health check returned ${response.status}`);
|
|
2822
3131
|
const data = await response.json();
|
|
2823
3132
|
const models = data.models || [];
|
|
2824
3133
|
const hasModel = models.some((m) => m.name === config.model);
|
|
2825
3134
|
return {
|
|
2826
3135
|
healthy: true,
|
|
2827
3136
|
provider: "ollama",
|
|
2828
|
-
capabilities: {
|
|
2829
|
-
model: config.model,
|
|
2830
|
-
available: hasModel,
|
|
2831
|
-
totalModels: models.length
|
|
2832
|
-
},
|
|
3137
|
+
capabilities: { model: config.model, available: hasModel, totalModels: models.length },
|
|
2833
3138
|
timestamp
|
|
2834
3139
|
};
|
|
2835
3140
|
} catch (error) {
|
|
@@ -2846,18 +3151,11 @@ var ProviderHealthCheck = class {
|
|
|
2846
3151
|
const baseUrlRaw = config.baseUrl || ((_a = config.options) == null ? void 0 : _a.baseUrl);
|
|
2847
3152
|
const baseUrl = (typeof baseUrlRaw === "string" ? baseUrlRaw : "").replace(/\/$/, "");
|
|
2848
3153
|
if (!baseUrl) {
|
|
2849
|
-
return {
|
|
2850
|
-
healthy: false,
|
|
2851
|
-
provider: config.provider,
|
|
2852
|
-
error: "baseUrl is required",
|
|
2853
|
-
timestamp
|
|
2854
|
-
};
|
|
3154
|
+
return { healthy: false, provider: config.provider, error: "baseUrl is required", timestamp };
|
|
2855
3155
|
}
|
|
2856
3156
|
try {
|
|
2857
3157
|
const headers = (_b = config.options) == null ? void 0 : _b.headers;
|
|
2858
|
-
const response = await fetch(`${baseUrl}/health`, {
|
|
2859
|
-
headers: headers || void 0
|
|
2860
|
-
});
|
|
3158
|
+
const response = await fetch(`${baseUrl}/health`, { headers: headers || void 0 });
|
|
2861
3159
|
return {
|
|
2862
3160
|
healthy: response.ok,
|
|
2863
3161
|
provider: config.provider,
|
|
@@ -2874,7 +3172,7 @@ var ProviderHealthCheck = class {
|
|
|
2874
3172
|
}
|
|
2875
3173
|
}
|
|
2876
3174
|
/**
|
|
2877
|
-
* Runs comprehensive health checks on all configured providers.
|
|
3175
|
+
* Runs comprehensive health checks on all configured providers in parallel.
|
|
2878
3176
|
*/
|
|
2879
3177
|
static async checkAll(vectorDbConfig, llmConfig, embeddingConfig) {
|
|
2880
3178
|
const [vectorDb, llm, embedding] = await Promise.all([
|