@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.
Files changed (51) hide show
  1. package/dist/{ChromaDBProvider-QNI7UCX4.mjs → ChromaDBProvider-2JZSBOQX.mjs} +1 -1
  2. package/dist/{MilvusProvider-OO6QGZDZ.mjs → MilvusProvider-CJDBCBVI.mjs} +1 -1
  3. package/dist/{PineconeProvider-ZRAFNFEC.mjs → PineconeProvider-E6L5Z2FO.mjs} +1 -1
  4. package/dist/{QdrantProvider-NYGHAA7C.mjs → QdrantProvider-JITRNJQN.mjs} +1 -1
  5. package/dist/{RagConfig-hBGXJmSx.d.mts → RagConfig-D_rSf8ep.d.mts} +1 -1
  6. package/dist/{RagConfig-hBGXJmSx.d.ts → RagConfig-D_rSf8ep.d.ts} +1 -1
  7. package/dist/{RedisProvider-ASONNYBI.mjs → RedisProvider-3VKFQXXD.mjs} +1 -1
  8. package/dist/UniversalVectorProvider-E6L4U4OX.mjs +9 -0
  9. package/dist/{WeaviateProvider-PSDCUGC7.mjs → WeaviateProvider-MXIPP44J.mjs} +1 -1
  10. package/dist/{chunk-5K23H7JL.mjs → chunk-26EMHLIN.mjs} +40 -2
  11. package/dist/{chunk-VPNRDXIA.mjs → chunk-7BQI4A5J.mjs} +17 -11
  12. package/dist/{chunk-ZM6TYIDH.mjs → chunk-MFWJZVF3.mjs} +3 -1
  13. package/dist/{chunk-V75V7BT2.mjs → chunk-TSX6DQXX.mjs} +2 -2
  14. package/dist/{chunk-HUGLYKD6.mjs → chunk-XZPVJS2B.mjs} +27 -9
  15. package/dist/chunk-Y6HQZDCJ.mjs +156 -0
  16. package/dist/{chunk-HSBXE2WV.mjs → chunk-YIYDJQJM.mjs} +745 -192
  17. package/dist/{chunk-7NXI6ZWX.mjs → chunk-YST6KYBJ.mjs} +8 -5
  18. package/dist/handlers/index.d.mts +2 -2
  19. package/dist/handlers/index.d.ts +2 -2
  20. package/dist/handlers/index.js +924 -626
  21. package/dist/handlers/index.mjs +1 -2
  22. package/dist/index-BJ8CUArE.d.mts +114 -0
  23. package/dist/index-DtNprGGj.d.ts +114 -0
  24. package/dist/index.d.mts +2 -2
  25. package/dist/index.d.ts +2 -2
  26. package/dist/server.d.mts +575 -17
  27. package/dist/server.d.ts +575 -17
  28. package/dist/server.js +1280 -643
  29. package/dist/server.mjs +314 -16
  30. package/package.json +11 -2
  31. package/src/config/ConfigBuilder.ts +373 -0
  32. package/src/config/EmbeddingStrategy.ts +147 -0
  33. package/src/config/serverConfig.ts +51 -17
  34. package/src/core/ConfigValidator.ts +77 -52
  35. package/src/core/Pipeline.ts +28 -26
  36. package/src/core/PluginManager.ts +277 -0
  37. package/src/core/ProviderHealthCheck.ts +79 -140
  38. package/src/core/ProviderRegistry.ts +38 -15
  39. package/src/providers/vectordb/ChromaDBProvider.ts +37 -12
  40. package/src/providers/vectordb/MilvusProvider.ts +25 -10
  41. package/src/providers/vectordb/PineconeProvider.ts +17 -2
  42. package/src/providers/vectordb/QdrantProvider.ts +46 -2
  43. package/src/providers/vectordb/RedisProvider.ts +34 -11
  44. package/src/providers/vectordb/UniversalVectorProvider.ts +220 -0
  45. package/src/providers/vectordb/WeaviateProvider.ts +17 -10
  46. package/src/server.ts +28 -10
  47. package/dist/LLMFactory-JFOY2V4X.mjs +0 -8
  48. package/dist/chunk-JI6VD5TJ.mjs +0 -387
  49. package/dist/index-Bx182KKn.d.ts +0 -64
  50. package/dist/index-Ck2pt7-8.d.mts +0 -64
  51. package/src/test-refactor.ts +0 -59
@@ -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(`[PineconeProvider] Index "${this.indexName}" not found.`);
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/uri is required");
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
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
937
- async deleteNamespace(_namespace) {
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
- const { data } = await this.http.get(`/api/v1/collections/${this.indexName}`);
1096
- this.collectionId = data.id;
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, _namespace) {
776
+ async delete(id, namespace) {
1132
777
  await this.http.post(`/api/v1/collections/${this.collectionId}/delete`, {
1133
778
  ids: [id],
1134
- where: _namespace ? { namespace: { $eq: _namespace } } : void 0
779
+ where: namespace ? { namespace: { $eq: namespace } } : void 0
1135
780
  });
1136
781
  }
1137
- async deleteNamespace(_namespace) {
782
+ async deleteNamespace(namespace) {
1138
783
  await this.http.post(`/api/v1/collections/${this.collectionId}/delete`, {
1139
- where: { namespace: { $eq: _namespace } }
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
- Authorization: `Bearer ${opts.apiKey}`
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
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1218
- async deleteNamespace(_namespace) {
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 false;
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 = ["pinecone", "pgvector", "postgresql", "mongodb", "milvus", "qdrant", "chromadb", "redis", "weaviate", "rest", "universal_rest", "custom"];
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
- vectorDbOptions.environment = (_d = readString(env, "PINECONE_ENVIRONMENT")) != null ? _d : "";
1403
- } else if (vectorProvider === "pgvector") {
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 = (_f = readString(env, "VECTOR_DB_REST_URL")) != null ? _f : "";
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 = (_h = (_g = readString(env, "VECTOR_BASE_URL")) != null ? _g : readString(env, "VECTOR_DB_REST_URL")) != null ? _h : "";
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
- custom: (_n = readString(env, "EMBEDDING_API_KEY")) != null ? _n : readString(env, "OPENAI_API_KEY")
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: (_o = readString(env, "VECTOR_DB_INDEX")) != null ? _o : "rag-index",
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: (_p = readString(env, "LLM_MODEL")) != null ? _p : "gpt-4o",
1443
- apiKey: (_q = llmApiKeyByProvider[llmProvider]) != null ? _q : "",
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: (_r = readString(env, "EMBEDDING_MODEL")) != null ? _r : "text-embedding-3-small",
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: (_t = (_s = readString(env, "NEXT_PUBLIC_UI_TITLE")) != null ? _s : readString(env, "UI_TITLE")) != null ? _t : "AI Assistant",
1464
- subtitle: (_v = (_u = readString(env, "NEXT_PUBLIC_UI_SUBTITLE")) != null ? _u : readString(env, "UI_SUBTITLE")) != null ? _v : "Powered by RAG",
1465
- primaryColor: (_x = (_w = readString(env, "NEXT_PUBLIC_PRIMARY_COLOR")) != null ? _w : readString(env, "UI_PRIMARY_COLOR")) != null ? _x : "#10b981",
1466
- accentColor: (_z = (_y = readString(env, "NEXT_PUBLIC_ACCENT_COLOR")) != null ? _y : readString(env, "UI_ACCENT_COLOR")) != null ? _z : "#3b82f6",
1467
- logoUrl: (_A = readString(env, "NEXT_PUBLIC_LOGO_URL")) != null ? _A : readString(env, "UI_LOGO_URL"),
1468
- placeholder: (_C = (_B = readString(env, "NEXT_PUBLIC_PLACEHOLDER")) != null ? _B : readString(env, "UI_PLACEHOLDER")) != null ? _C : "Ask me anything\u2026",
1469
- showSources: ((_E = (_D = readString(env, "NEXT_PUBLIC_SHOW_SOURCES")) != null ? _D : readString(env, "UI_SHOW_SOURCES")) != null ? _E : "true") !== "false",
1470
- welcomeMessage: (_G = (_F = readString(env, "NEXT_PUBLIC_WELCOME_MESSAGE")) != null ? _F : readString(env, "UI_WELCOME_MESSAGE")) != null ? _G : "Hello! I'm your AI assistant. Ask me anything about your documents.",
1471
- visualStyle: (_I = (_H = readString(env, "NEXT_PUBLIC_UI_VISUAL_STYLE")) != null ? _H : readString(env, "UI_VISUAL_STYLE")) != null ? _I : "glass",
1472
- borderRadius: (_K = (_J = readString(env, "NEXT_PUBLIC_UI_BORDER_RADIUS")) != null ? _J : readString(env, "UI_BORDER_RADIUS")) != null ? _K : "xl"
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
- if (!opts.host) {
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.host",
1679
- message: "Milvus host is required",
1680
- suggestion: "Set MILVUS_HOST environment variable",
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.host && !opts.path) {
1551
+ if (!opts.baseUrl && !opts.host) {
1711
1552
  errors.push({
1712
- field: "vectorDb.options.host",
1713
- message: "ChromaDB host or path is required",
1714
- suggestion: "Set CHROMADB_HOST or CHROMADB_PATH environment variable",
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
- if (!opts.url && (!opts.host || !opts.port)) {
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.url",
1726
- message: "Redis connection URL or host:port is required",
1727
- suggestion: "Set REDIS_URL or REDIS_HOST/REDIS_PORT environment variables",
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.url",
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 s = new Option().style;
1958
- s.color = color;
1959
- return s.color !== "";
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
- case "mongodb":
2305
+ }
2306
+ case "mongodb": {
2069
2307
  const { MongoDBProvider: MongoDBProvider2 } = await Promise.resolve().then(() => (init_MongoDBProvider(), MongoDBProvider_exports));
2070
2308
  return new MongoDBProvider2(config);
2071
- case "milvus":
2309
+ }
2310
+ case "milvus": {
2072
2311
  const { MilvusProvider: MilvusProvider2 } = await Promise.resolve().then(() => (init_MilvusProvider(), MilvusProvider_exports));
2073
2312
  return new MilvusProvider2(config);
2074
- case "qdrant":
2313
+ }
2314
+ case "qdrant": {
2075
2315
  const { QdrantProvider: QdrantProvider2 } = await Promise.resolve().then(() => (init_QdrantProvider(), QdrantProvider_exports));
2076
2316
  return new QdrantProvider2(config);
2077
- case "chromadb":
2317
+ }
2318
+ case "chromadb": {
2078
2319
  const { ChromaDBProvider: ChromaDBProvider2 } = await Promise.resolve().then(() => (init_ChromaDBProvider(), ChromaDBProvider_exports));
2079
2320
  return new ChromaDBProvider2(config);
2080
- case "redis":
2321
+ }
2322
+ case "redis": {
2081
2323
  const { RedisProvider: RedisProvider2 } = await Promise.resolve().then(() => (init_RedisProvider(), RedisProvider_exports));
2082
2324
  return new RedisProvider2(config);
2083
- case "weaviate":
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(`[ProviderRegistry] Unsupported vector provider: ${provider}`);
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
- this.llmProvider = ProviderRegistry.createLLMProvider(this.config.llm, this.config.embedding);
2338
- if (this.config.llm.provider === "anthropic") {
2339
- const { LLMFactory: LLMFactory2 } = await Promise.resolve().then(() => (init_LLMFactory(), LLMFactory_exports));
2340
- this.embeddingProvider = LLMFactory2.createEmbeddingProvider(this.config.embedding);
2341
- } else {
2342
- this.embeddingProvider = this.llmProvider;
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 processing.
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 batchOptions = {
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
- batchOptions
2696
+ embedBatchOptions
2371
2697
  );
2372
2698
  if (vectors.length !== chunks.length) {
2373
- throw new Error(`Embedding failed: got ${vectors.length} vectors for ${chunks.length} chunks`);
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 host = opts.host || "localhost";
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(`http://${host}:${port}/healthz`, {
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: `${host}:${port}` },
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 response = await fetch(`${baseUrl}/health`);
2617
- if (!response.ok) {
2618
- throw new Error(`Health check returned ${response.status}`);
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 host = opts.host || "localhost";
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(`http://${host}:${port}/api/v1/heartbeat`);
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: `${host}:${port}` },
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
- try {
2664
- await fetch(url);
2665
- return {
2666
- healthy: true,
2667
- provider: "redis",
2668
- capabilities: { endpoint: url },
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 url = (opts.url || "http://localhost:8080").replace(/\/$/, "");
3009
+ const baseUrl = (opts.baseUrl || opts.url || "http://localhost:8080").replace(/\/$/, "");
2683
3010
  try {
2684
- const response = await fetch(`${url}/.well-known/ready`);
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: url },
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([