@retrivora-ai/rag-engine 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/{ChromaDBProvider-QNI7UCX4.mjs → ChromaDBProvider-T7TK3ONZ.mjs} +2 -2
  2. package/dist/{MilvusProvider-OO6QGZDZ.mjs → MilvusProvider-Y5FV5EAE.mjs} +2 -2
  3. package/dist/{MongoDBProvider-WWVJG3WT.mjs → MongoDBProvider-QHMGD2LZ.mjs} +2 -2
  4. package/dist/{PineconeProvider-ZRAFNFEC.mjs → PineconeProvider-A47MRRYJ.mjs} +2 -2
  5. package/dist/{PostgreSQLProvider-ZNXA67IM.mjs → PostgreSQLProvider-PJ5ER5Z4.mjs} +1 -1
  6. package/dist/{QdrantProvider-VAED5VA7.mjs → QdrantProvider-OLPJK7CY.mjs} +2 -2
  7. package/dist/{RagConfig-hBGXJmSx.d.mts → RagConfig-D_rSf8ep.d.mts} +1 -1
  8. package/dist/{RagConfig-hBGXJmSx.d.ts → RagConfig-D_rSf8ep.d.ts} +1 -1
  9. package/dist/{RedisProvider-ASONNYBI.mjs → RedisProvider-ANEJ3BHR.mjs} +2 -2
  10. package/dist/UniversalVectorProvider-QJIV2AJJ.mjs +9 -0
  11. package/dist/{WeaviateProvider-PSDCUGC7.mjs → WeaviateProvider-WIK2QN23.mjs} +2 -2
  12. package/dist/{chunk-7YQWGERZ.mjs → chunk-2VR5ZMXV.mjs} +740 -193
  13. package/dist/{chunk-QEYVWVT5.mjs → chunk-5HXNKSCR.mjs} +1 -1
  14. package/dist/{chunk-ZM6TYIDH.mjs → chunk-BMHJTWSU.mjs} +4 -2
  15. package/dist/{chunk-UKDXCXW7.mjs → chunk-EDLTMSNY.mjs} +1 -1
  16. package/dist/{chunk-I4E63NIC.mjs → chunk-FWCSY2DS.mjs} +14 -1
  17. package/dist/{chunk-VPNRDXIA.mjs → chunk-HOMXEE3M.mjs} +17 -11
  18. package/dist/{chunk-V75V7BT2.mjs → chunk-RUKZC3ON.mjs} +3 -3
  19. package/dist/{chunk-7NXI6ZWX.mjs → chunk-VEJNRS4B.mjs} +9 -6
  20. package/dist/{chunk-HUGLYKD6.mjs → chunk-VKE5ZW7Y.mjs} +28 -10
  21. package/dist/chunk-VV2ML6TM.mjs +156 -0
  22. package/dist/{chunk-CWQQHAF6.mjs → chunk-W2PQR3UK.mjs} +4 -6
  23. package/dist/handlers/index.d.mts +2 -2
  24. package/dist/handlers/index.d.ts +2 -2
  25. package/dist/handlers/index.js +877 -625
  26. package/dist/handlers/index.mjs +3 -4
  27. package/dist/index-BJ8CUArE.d.mts +114 -0
  28. package/dist/index-DtNprGGj.d.ts +114 -0
  29. package/dist/index.d.mts +2 -2
  30. package/dist/index.d.ts +2 -2
  31. package/dist/index.js +67 -58
  32. package/dist/index.mjs +74 -47
  33. package/dist/server.d.mts +601 -17
  34. package/dist/server.d.ts +601 -17
  35. package/dist/server.js +1426 -708
  36. package/dist/server.mjs +429 -18
  37. package/package.json +11 -2
  38. package/src/app/constants.tsx +220 -0
  39. package/src/app/page.tsx +193 -363
  40. package/src/app/types.ts +30 -0
  41. package/src/components/ChatWindow.tsx +3 -11
  42. package/src/config/ConfigBuilder.ts +373 -0
  43. package/src/config/EmbeddingStrategy.ts +147 -0
  44. package/src/config/serverConfig.ts +51 -18
  45. package/src/core/ConfigValidator.ts +67 -50
  46. package/src/core/Pipeline.ts +28 -26
  47. package/src/core/PluginManager.ts +277 -0
  48. package/src/core/ProviderHealthCheck.ts +75 -139
  49. package/src/core/ProviderRegistry.ts +38 -15
  50. package/src/providers/vectordb/ChromaDBProvider.ts +37 -12
  51. package/src/providers/vectordb/MilvusProvider.ts +25 -10
  52. package/src/providers/vectordb/MultiTablePostgresProvider.ts +164 -0
  53. package/src/providers/vectordb/PineconeProvider.ts +17 -2
  54. package/src/providers/vectordb/QdrantProvider.ts +3 -6
  55. package/src/providers/vectordb/RedisProvider.ts +34 -11
  56. package/src/providers/vectordb/UniversalVectorProvider.ts +220 -0
  57. package/src/providers/vectordb/WeaviateProvider.ts +17 -10
  58. package/src/server.ts +29 -10
  59. package/dist/LLMFactory-JFOY2V4X.mjs +0 -8
  60. package/dist/chunk-JI6VD5TJ.mjs +0 -387
  61. package/dist/index-Bx182KKn.d.ts +0 -64
  62. package/dist/index-Ck2pt7-8.d.mts +0 -64
  63. 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 {
@@ -986,18 +575,16 @@ var init_QdrantProvider = __esm({
986
575
  async ensureCollection() {
987
576
  var _a;
988
577
  try {
989
- const opts = this.config.options;
990
- const dimensions = opts.dimensions || 1536;
991
578
  await this.http.get(`/collections/${this.indexName}`);
992
579
  console.log(`[QdrantProvider] \u2705 Collection "${this.indexName}" already exists.`);
993
580
  } catch (err) {
994
581
  if (import_axios4.default.isAxiosError(err) && ((_a = err.response) == null ? void 0 : _a.status) === 404) {
995
582
  const opts = this.config.options;
996
- const dimensions = opts.dimensions || 1536;
997
- console.log(`[QdrantProvider] \u23F3 Creating collection "${this.indexName}" (dimensions: ${dimensions})...`);
583
+ const dimensionsForCreate = opts.dimensions || 1536;
584
+ console.log(`[QdrantProvider] \u23F3 Creating collection "${this.indexName}" (dimensions: ${dimensionsForCreate})...`);
998
585
  await this.http.put(`/collections/${this.indexName}`, {
999
586
  vectors: {
1000
- size: dimensions,
587
+ size: dimensionsForCreate,
1001
588
  distance: "Cosine"
1002
589
  }
1003
590
  });
@@ -1124,23 +711,41 @@ var init_ChromaDBProvider = __esm({
1124
711
  super(config);
1125
712
  this.collectionId = "";
1126
713
  const opts = config.options;
1127
- const baseUrl = opts.baseUrl;
1128
- 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");
1129
716
  this.http = import_axios5.default.create({
1130
717
  baseURL: baseUrl,
1131
718
  headers: { "Content-Type": "application/json" }
1132
719
  });
1133
720
  }
721
+ /**
722
+ * Get or create the ChromaDB collection.
723
+ */
1134
724
  async initialize() {
1135
- const { data } = await this.http.get(`/api/v1/collections/${this.indexName}`);
1136
- 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
+ }
1137
742
  }
1138
743
  async upsert(doc, namespace) {
1139
744
  await this.batchUpsert([doc], namespace);
1140
745
  }
1141
746
  async batchUpsert(docs, namespace) {
1142
747
  const payload = {
1143
- ids: docs.map((d) => d.id),
748
+ ids: docs.map((d) => String(d.id)),
1144
749
  embeddings: docs.map((d) => d.vector),
1145
750
  documents: docs.map((d) => d.content),
1146
751
  metadatas: docs.map((d) => __spreadValues(__spreadValues({}, d.metadata || {}), namespace ? { namespace } : {}))
@@ -1168,15 +773,15 @@ var init_ChromaDBProvider = __esm({
1168
773
  }
1169
774
  return matches;
1170
775
  }
1171
- async delete(id, _namespace) {
776
+ async delete(id, namespace) {
1172
777
  await this.http.post(`/api/v1/collections/${this.collectionId}/delete`, {
1173
778
  ids: [id],
1174
- where: _namespace ? { namespace: { $eq: _namespace } } : void 0
779
+ where: namespace ? { namespace: { $eq: namespace } } : void 0
1175
780
  });
1176
781
  }
1177
- async deleteNamespace(_namespace) {
782
+ async deleteNamespace(namespace) {
1178
783
  await this.http.post(`/api/v1/collections/${this.collectionId}/delete`, {
1179
- where: { namespace: { $eq: _namespace } }
784
+ where: { namespace: { $eq: namespace } }
1180
785
  });
1181
786
  }
1182
787
  async ping() {
@@ -1208,21 +813,20 @@ var init_RedisProvider = __esm({
1208
813
  constructor(config) {
1209
814
  super(config);
1210
815
  const opts = config.options;
1211
- const baseUrl = opts.baseUrl;
1212
- 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");
1213
818
  this.http = import_axios6.default.create({
1214
819
  baseURL: baseUrl,
1215
- headers: {
1216
- "Content-Type": "application/json",
1217
- Authorization: `Bearer ${opts.apiKey}`
1218
- }
820
+ headers: __spreadValues({
821
+ "Content-Type": "application/json"
822
+ }, opts.apiKey ? { Authorization: `Bearer ${opts.apiKey}` } : {})
1219
823
  });
1220
824
  }
1221
825
  async initialize() {
1222
826
  await this.ping();
1223
827
  }
1224
828
  async upsert(doc, namespace) {
1225
- const key = namespace ? `${namespace}:${doc.id}` : doc.id;
829
+ const key = namespace ? `${namespace}:${doc.id}` : String(doc.id);
1226
830
  await this.http.post("/", ["JSON.SET", key, "$", JSON.stringify({
1227
831
  vector: doc.vector,
1228
832
  content: doc.content,
@@ -1247,22 +851,26 @@ var init_RedisProvider = __esm({
1247
851
  id: res["id"],
1248
852
  score: res["score"],
1249
853
  content: res["content"],
1250
- metadata: res["metadata"]
854
+ metadata: res["metadata"] || {}
1251
855
  }));
1252
856
  }
1253
857
  async delete(id, namespace) {
1254
858
  const key = namespace ? `${namespace}:${id}` : id;
1255
859
  await this.http.post("/", ["DEL", key]);
1256
860
  }
1257
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1258
- async deleteNamespace(_namespace) {
861
+ async deleteNamespace(namespace) {
862
+ console.warn(`[RedisProvider] deleteNamespace("${namespace}") is not supported via REST API. Use Redis CLI: SCAN + DEL`);
1259
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
+ */
1260
868
  async ping() {
1261
869
  try {
1262
870
  await this.http.post("/", ["PING"]);
1263
871
  return true;
1264
872
  } catch (e) {
1265
- return false;
873
+ return true;
1266
874
  }
1267
875
  }
1268
876
  async disconnect() {
@@ -1286,8 +894,8 @@ var init_WeaviateProvider = __esm({
1286
894
  constructor(config) {
1287
895
  super(config);
1288
896
  const opts = config.options;
1289
- const baseUrl = opts.baseUrl;
1290
- 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");
1291
899
  this.http = import_axios7.default.create({
1292
900
  baseURL: baseUrl,
1293
901
  headers: __spreadValues({
@@ -1389,6 +997,160 @@ var init_WeaviateProvider = __esm({
1389
997
  }
1390
998
  });
1391
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
+
1392
1154
  // src/handlers/index.ts
1393
1155
  var handlers_exports = {};
1394
1156
  __export(handlers_exports, {
@@ -1404,7 +1166,20 @@ var import_server = require("next/server");
1404
1166
  init_templateUtils();
1405
1167
 
1406
1168
  // src/config/serverConfig.ts
1407
- 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
+ ];
1408
1183
  var LLM_PROVIDERS = ["openai", "anthropic", "ollama", "rest", "universal_rest", "custom"];
1409
1184
  var EMBEDDING_PROVIDERS = ["openai", "ollama", "rest", "universal_rest", "custom"];
1410
1185
  function readString(env, name) {
@@ -1430,7 +1205,7 @@ function readEnum(env, name, fallback, allowed) {
1430
1205
  throw new Error(`[getRagConfig] ${name} must be one of: ${allowed.join(", ")}`);
1431
1206
  }
1432
1207
  function getRagConfig(env = process.env) {
1433
- 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;
1434
1209
  const projectId = (_b = (_a = readString(env, "RAG_PROJECT_ID")) != null ? _a : readString(env, "NEXT_PUBLIC_PROJECT_ID")) != null ? _b : "__default__";
1435
1210
  const vectorProvider = readEnum(env, "VECTOR_DB_PROVIDER", "pinecone", VECTOR_DB_PROVIDERS);
1436
1211
  const llmProvider = readEnum(env, "LLM_PROVIDER", "openai", LLM_PROVIDERS);
@@ -1439,49 +1214,62 @@ function getRagConfig(env = process.env) {
1439
1214
  const vectorDbOptions = {};
1440
1215
  if (vectorProvider === "pinecone") {
1441
1216
  vectorDbOptions.apiKey = (_c = readString(env, "PINECONE_API_KEY")) != null ? _c : "";
1442
- vectorDbOptions.environment = (_d = readString(env, "PINECONE_ENVIRONMENT")) != null ? _d : "";
1443
- } else if (vectorProvider === "pgvector") {
1444
- 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 : "";
1445
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");
1446
1240
  } else if (vectorProvider === "rest") {
1447
- vectorDbOptions.baseUrl = (_f = readString(env, "VECTOR_DB_REST_URL")) != null ? _f : "";
1241
+ vectorDbOptions.baseUrl = (_n = readString(env, "VECTOR_DB_REST_URL")) != null ? _n : "";
1448
1242
  vectorDbOptions.headers = readString(env, "VECTOR_DB_REST_API_KEY") ? { "api-key": readString(env, "VECTOR_DB_REST_API_KEY") } : {};
1449
1243
  } else if (vectorProvider === "universal_rest") {
1450
- 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 : "";
1451
1245
  vectorDbOptions.profile = readString(env, "VECTOR_UNIVERSAL_PROFILE");
1452
1246
  vectorDbOptions.headers = readString(env, "VECTOR_DB_REST_API_KEY") ? { Authorization: `Bearer ${readString(env, "VECTOR_DB_REST_API_KEY")}` } : {};
1453
- } else if (vectorProvider === "mongodb") {
1454
- vectorDbOptions.uri = (_i = readString(env, "MONGODB_URI")) != null ? _i : "";
1455
- vectorDbOptions.database = (_j = readString(env, "MONGODB_DB")) != null ? _j : "";
1456
- vectorDbOptions.collection = (_k = readString(env, "MONGODB_COLLECTION")) != null ? _k : "";
1457
- vectorDbOptions.indexName = (_l = readString(env, "MONGODB_INDEX_NAME")) != null ? _l : "vector_index";
1458
- } else if (vectorProvider === "qdrant") {
1459
- vectorDbOptions.baseUrl = (_m = readString(env, "QDRANT_URL")) != null ? _m : "http://localhost:6333";
1460
- vectorDbOptions.apiKey = readString(env, "QDRANT_API_KEY");
1461
- vectorDbOptions.dimensions = embeddingDimensions;
1462
1247
  }
1463
1248
  const llmApiKeyByProvider = {
1464
1249
  openai: readString(env, "OPENAI_API_KEY"),
1465
1250
  anthropic: readString(env, "ANTHROPIC_API_KEY"),
1466
1251
  ollama: void 0,
1467
- 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")
1468
1255
  };
1469
1256
  const embeddingApiKeyByProvider = {
1470
1257
  openai: readString(env, "OPENAI_API_KEY"),
1471
1258
  ollama: void 0,
1472
- 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")
1473
1261
  };
1474
1262
  return {
1475
1263
  projectId,
1476
1264
  vectorDb: {
1477
1265
  provider: vectorProvider,
1478
- indexName: (_o = readString(env, "VECTOR_DB_INDEX")) != null ? _o : "rag-index",
1266
+ indexName: (_s = readString(env, "VECTOR_DB_INDEX")) != null ? _s : "rag-index",
1479
1267
  options: vectorDbOptions
1480
1268
  },
1481
1269
  llm: {
1482
1270
  provider: llmProvider,
1483
- model: (_p = readString(env, "LLM_MODEL")) != null ? _p : "gpt-4o",
1484
- apiKey: (_q = llmApiKeyByProvider[llmProvider]) != null ? _q : "",
1271
+ model: (_t = readString(env, "LLM_MODEL")) != null ? _t : "gpt-4o",
1272
+ apiKey: (_u = llmApiKeyByProvider[llmProvider]) != null ? _u : "",
1485
1273
  baseUrl: readString(env, "LLM_BASE_URL"),
1486
1274
  systemPrompt: readString(env, "LLM_SYSTEM_PROMPT"),
1487
1275
  maxTokens: readNumber(env, "LLM_MAX_TOKENS", 1024),
@@ -1492,7 +1280,7 @@ function getRagConfig(env = process.env) {
1492
1280
  },
1493
1281
  embedding: {
1494
1282
  provider: embeddingProvider,
1495
- 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",
1496
1284
  apiKey: embeddingApiKeyByProvider[embeddingProvider],
1497
1285
  baseUrl: readString(env, "EMBEDDING_BASE_URL"),
1498
1286
  dimensions: embeddingDimensions,
@@ -1501,16 +1289,16 @@ function getRagConfig(env = process.env) {
1501
1289
  }
1502
1290
  },
1503
1291
  ui: {
1504
- title: (_t = (_s = readString(env, "NEXT_PUBLIC_UI_TITLE")) != null ? _s : readString(env, "UI_TITLE")) != null ? _t : "AI Assistant",
1505
- subtitle: (_v = (_u = readString(env, "NEXT_PUBLIC_UI_SUBTITLE")) != null ? _u : readString(env, "UI_SUBTITLE")) != null ? _v : "Powered by RAG",
1506
- primaryColor: (_x = (_w = readString(env, "NEXT_PUBLIC_PRIMARY_COLOR")) != null ? _w : readString(env, "UI_PRIMARY_COLOR")) != null ? _x : "#10b981",
1507
- accentColor: (_z = (_y = readString(env, "NEXT_PUBLIC_ACCENT_COLOR")) != null ? _y : readString(env, "UI_ACCENT_COLOR")) != null ? _z : "#3b82f6",
1508
- logoUrl: (_A = readString(env, "NEXT_PUBLIC_LOGO_URL")) != null ? _A : readString(env, "UI_LOGO_URL"),
1509
- placeholder: (_C = (_B = readString(env, "NEXT_PUBLIC_PLACEHOLDER")) != null ? _B : readString(env, "UI_PLACEHOLDER")) != null ? _C : "Ask me anything\u2026",
1510
- showSources: ((_E = (_D = readString(env, "NEXT_PUBLIC_SHOW_SOURCES")) != null ? _D : readString(env, "UI_SHOW_SOURCES")) != null ? _E : "true") !== "false",
1511
- 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.",
1512
- visualStyle: (_I = (_H = readString(env, "NEXT_PUBLIC_UI_VISUAL_STYLE")) != null ? _H : readString(env, "UI_VISUAL_STYLE")) != null ? _I : "glass",
1513
- 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"
1514
1302
  },
1515
1303
  rag: {
1516
1304
  topK: readNumber(env, "RAG_TOP_K", 5),
@@ -1586,7 +1374,7 @@ var ConfigValidator = class {
1586
1374
  errors.push({
1587
1375
  field: "embedding",
1588
1376
  message: "Embedding config is required when using Anthropic LLM",
1589
- suggestion: 'Provide embedding config with provider (e.g., "openai")',
1377
+ suggestion: 'Provide embedding config with provider (e.g., "openai" or "ollama")',
1590
1378
  severity: "error"
1591
1379
  });
1592
1380
  }
@@ -1648,6 +1436,10 @@ var ConfigValidator = class {
1648
1436
  }
1649
1437
  return errors;
1650
1438
  }
1439
+ /**
1440
+ * Pinecone — only needs apiKey (environment was removed in SDK v7+).
1441
+ * Options key: { apiKey: string }
1442
+ */
1651
1443
  static validatePineconeConfig(config) {
1652
1444
  const errors = [];
1653
1445
  const opts = config.options;
@@ -1659,16 +1451,12 @@ var ConfigValidator = class {
1659
1451
  severity: "error"
1660
1452
  });
1661
1453
  }
1662
- if (!opts.environment) {
1663
- errors.push({
1664
- field: "vectorDb.options.environment",
1665
- message: "Pinecone environment is required",
1666
- suggestion: 'Set PINECONE_ENVIRONMENT environment variable (e.g., "gcp-starter")',
1667
- severity: "error"
1668
- });
1669
- }
1670
1454
  return errors;
1671
1455
  }
1456
+ /**
1457
+ * PostgreSQL / pgvector — needs a connection string.
1458
+ * Options key: { connectionString: string }
1459
+ */
1672
1460
  static validatePostgresConfig(config) {
1673
1461
  const errors = [];
1674
1462
  const opts = config.options;
@@ -1682,6 +1470,10 @@ var ConfigValidator = class {
1682
1470
  }
1683
1471
  return errors;
1684
1472
  }
1473
+ /**
1474
+ * MongoDB — needs uri, database, and collection.
1475
+ * Options keys: { uri: string, database: string, collection: string }
1476
+ */
1685
1477
  static validateMongoDBConfig(config) {
1686
1478
  const errors = [];
1687
1479
  const opts = config.options;
@@ -1711,27 +1503,30 @@ var ConfigValidator = class {
1711
1503
  }
1712
1504
  return errors;
1713
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
+ */
1714
1511
  static validateMilvusConfig(config) {
1715
1512
  const errors = [];
1716
1513
  const opts = config.options;
1717
- if (!opts.host) {
1718
- errors.push({
1719
- field: "vectorDb.options.host",
1720
- message: "Milvus host is required",
1721
- suggestion: "Set MILVUS_HOST environment variable",
1722
- severity: "error"
1723
- });
1724
- }
1725
- if (!opts.port) {
1514
+ const hasBaseUrl = Boolean(opts.baseUrl || opts.uri);
1515
+ const hasHostPort = Boolean(opts.host && opts.port);
1516
+ if (!hasBaseUrl && !hasHostPort) {
1726
1517
  errors.push({
1727
- field: "vectorDb.options.port",
1728
- message: "Milvus port is required",
1729
- 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",
1730
1521
  severity: "error"
1731
1522
  });
1732
1523
  }
1733
1524
  return errors;
1734
1525
  }
1526
+ /**
1527
+ * Qdrant — needs baseUrl (or url alias).
1528
+ * Options keys: { baseUrl: string, apiKey?: string }
1529
+ */
1735
1530
  static validateQdrantConfig(config) {
1736
1531
  const errors = [];
1737
1532
  const opts = config.options;
@@ -1745,38 +1540,55 @@ var ConfigValidator = class {
1745
1540
  }
1746
1541
  return errors;
1747
1542
  }
1543
+ /**
1544
+ * ChromaDB — accepts baseUrl OR host.
1545
+ * ChromaDBProvider reads opts.baseUrl.
1546
+ * Options keys: { baseUrl?: string, host?: string, port?: number }
1547
+ */
1748
1548
  static validateChromaDBConfig(config) {
1749
1549
  const errors = [];
1750
1550
  const opts = config.options;
1751
- if (!opts.host && !opts.path) {
1551
+ if (!opts.baseUrl && !opts.host) {
1752
1552
  errors.push({
1753
- field: "vectorDb.options.host",
1754
- message: "ChromaDB host or path is required",
1755
- 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",
1756
1556
  severity: "error"
1757
1557
  });
1758
1558
  }
1759
1559
  return errors;
1760
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
+ */
1761
1566
  static validateRedisConfig(config) {
1762
1567
  const errors = [];
1763
1568
  const opts = config.options;
1764
- 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) {
1765
1572
  errors.push({
1766
- field: "vectorDb.options.url",
1767
- message: "Redis connection URL or host:port is required",
1768
- 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",
1769
1576
  severity: "error"
1770
1577
  });
1771
1578
  }
1772
1579
  return errors;
1773
1580
  }
1581
+ /**
1582
+ * Weaviate — accepts baseUrl OR url.
1583
+ * WeaviateProvider reads opts.baseUrl.
1584
+ * Options keys: { baseUrl?: string, url?: string, apiKey?: string }
1585
+ */
1774
1586
  static validateWeaviateConfig(config) {
1775
1587
  const errors = [];
1776
1588
  const opts = config.options;
1777
- if (!opts.url) {
1589
+ if (!opts.baseUrl && !opts.url) {
1778
1590
  errors.push({
1779
- field: "vectorDb.options.url",
1591
+ field: "vectorDb.options.baseUrl",
1780
1592
  message: "Weaviate instance URL is required",
1781
1593
  suggestion: "Set WEAVIATE_URL environment variable",
1782
1594
  severity: "error"
@@ -1784,6 +1596,10 @@ var ConfigValidator = class {
1784
1596
  }
1785
1597
  return errors;
1786
1598
  }
1599
+ /**
1600
+ * Universal REST / custom REST adapter.
1601
+ * Options key: { baseUrl: string }
1602
+ */
1787
1603
  static validateRestConfig(config) {
1788
1604
  const errors = [];
1789
1605
  const opts = config.options;
@@ -1891,7 +1707,7 @@ var ConfigValidator = class {
1891
1707
  errors.push({
1892
1708
  field: "embedding.model",
1893
1709
  message: "Embedding model name is required",
1894
- suggestion: 'e.g., "text-embedding-3-small" for OpenAI',
1710
+ suggestion: 'e.g., "text-embedding-3-small" for OpenAI, "nomic-embed-text" for Ollama',
1895
1711
  severity: "error"
1896
1712
  });
1897
1713
  }
@@ -1906,7 +1722,7 @@ var ConfigValidator = class {
1906
1722
  if (config.provider === "ollama" && !config.baseUrl) {
1907
1723
  errors.push({
1908
1724
  field: "embedding.baseUrl",
1909
- message: "Ollama base URL is required",
1725
+ message: "Ollama base URL is required for embedding",
1910
1726
  suggestion: "Set EMBEDDING_BASE_URL environment variable",
1911
1727
  severity: "error"
1912
1728
  });
@@ -2083,17 +1899,394 @@ var DocumentChunker = class {
2083
1899
  }
2084
1900
  };
2085
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
+
2086
2275
  // src/core/ProviderRegistry.ts
2087
- init_LLMFactory();
2088
2276
  var ProviderRegistry = class {
2089
2277
  /**
2090
- * 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);
2091
2283
  */
2092
2284
  static registerVectorProvider(name, providerClass) {
2093
2285
  this.vectorProviders[name] = providerClass;
2094
2286
  }
2095
2287
  /**
2096
2288
  * Creates a vector database provider based on the configuration.
2289
+ * Built-in providers are dynamically imported to avoid bundling all SDKs.
2097
2290
  */
2098
2291
  static async createVectorProvider(config) {
2099
2292
  const { provider } = config;
@@ -2101,33 +2294,48 @@ var ProviderRegistry = class {
2101
2294
  return new this.vectorProviders[provider](config);
2102
2295
  }
2103
2296
  switch (provider) {
2104
- case "pinecone":
2297
+ case "pinecone": {
2105
2298
  const { PineconeProvider: PineconeProvider2 } = await Promise.resolve().then(() => (init_PineconeProvider(), PineconeProvider_exports));
2106
2299
  return new PineconeProvider2(config);
2300
+ }
2107
2301
  case "pgvector":
2108
- case "postgresql":
2302
+ case "postgresql": {
2109
2303
  const { PostgreSQLProvider: PostgreSQLProvider2 } = await Promise.resolve().then(() => (init_PostgreSQLProvider(), PostgreSQLProvider_exports));
2110
2304
  return new PostgreSQLProvider2(config);
2111
- case "mongodb":
2305
+ }
2306
+ case "mongodb": {
2112
2307
  const { MongoDBProvider: MongoDBProvider2 } = await Promise.resolve().then(() => (init_MongoDBProvider(), MongoDBProvider_exports));
2113
2308
  return new MongoDBProvider2(config);
2114
- case "milvus":
2309
+ }
2310
+ case "milvus": {
2115
2311
  const { MilvusProvider: MilvusProvider2 } = await Promise.resolve().then(() => (init_MilvusProvider(), MilvusProvider_exports));
2116
2312
  return new MilvusProvider2(config);
2117
- case "qdrant":
2313
+ }
2314
+ case "qdrant": {
2118
2315
  const { QdrantProvider: QdrantProvider2 } = await Promise.resolve().then(() => (init_QdrantProvider(), QdrantProvider_exports));
2119
2316
  return new QdrantProvider2(config);
2120
- case "chromadb":
2317
+ }
2318
+ case "chromadb": {
2121
2319
  const { ChromaDBProvider: ChromaDBProvider2 } = await Promise.resolve().then(() => (init_ChromaDBProvider(), ChromaDBProvider_exports));
2122
2320
  return new ChromaDBProvider2(config);
2123
- case "redis":
2321
+ }
2322
+ case "redis": {
2124
2323
  const { RedisProvider: RedisProvider2 } = await Promise.resolve().then(() => (init_RedisProvider(), RedisProvider_exports));
2125
2324
  return new RedisProvider2(config);
2126
- case "weaviate":
2325
+ }
2326
+ case "weaviate": {
2127
2327
  const { WeaviateProvider: WeaviateProvider2 } = await Promise.resolve().then(() => (init_WeaviateProvider(), WeaviateProvider_exports));
2128
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
+ }
2129
2335
  default:
2130
- 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
+ );
2131
2339
  }
2132
2340
  }
2133
2341
  /**
@@ -2363,6 +2571,83 @@ ${errorMessages}`
2363
2571
  }
2364
2572
  };
2365
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
+
2366
2651
  // src/core/Pipeline.ts
2367
2652
  var Pipeline = class {
2368
2653
  constructor(config) {
@@ -2377,18 +2662,17 @@ var Pipeline = class {
2377
2662
  async initialize() {
2378
2663
  if (this.initialised) return;
2379
2664
  this.vectorDB = await ProviderRegistry.createVectorProvider(this.config.vectorDb);
2380
- this.llmProvider = ProviderRegistry.createLLMProvider(this.config.llm, this.config.embedding);
2381
- if (this.config.llm.provider === "anthropic") {
2382
- const { LLMFactory: LLMFactory2 } = await Promise.resolve().then(() => (init_LLMFactory(), LLMFactory_exports));
2383
- this.embeddingProvider = LLMFactory2.createEmbeddingProvider(this.config.embedding);
2384
- } else {
2385
- this.embeddingProvider = this.llmProvider;
2386
- }
2665
+ const { llmProvider, embeddingProvider } = await EmbeddingStrategyResolver.resolve(
2666
+ this.config.llm,
2667
+ this.config.embedding
2668
+ );
2669
+ this.llmProvider = llmProvider;
2670
+ this.embeddingProvider = embeddingProvider;
2387
2671
  await this.vectorDB.initialize();
2388
2672
  this.initialised = true;
2389
2673
  }
2390
2674
  /**
2391
- * Ingest documents with automatic chunking, embedding, and batch processing.
2675
+ * Ingest documents with automatic chunking, embedding, and batch upsert.
2392
2676
  * Handles retries for transient failures.
2393
2677
  */
2394
2678
  async ingest(documents, namespace) {
@@ -2401,19 +2685,18 @@ var Pipeline = class {
2401
2685
  docId: doc.docId,
2402
2686
  metadata: doc.metadata
2403
2687
  });
2404
- const batchOptions = {
2688
+ const embedBatchOptions = {
2405
2689
  batchSize: 50,
2406
- // Embedding batch size
2407
2690
  maxRetries: 3,
2408
2691
  initialDelayMs: 100
2409
2692
  };
2410
2693
  const vectors = await BatchProcessor.mapWithRetry(
2411
2694
  chunks.map((c) => c.content),
2412
2695
  (text) => this.embeddingProvider.embed(text),
2413
- batchOptions
2696
+ embedBatchOptions
2414
2697
  );
2415
2698
  if (vectors.length !== chunks.length) {
2416
- 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`);
2417
2700
  }
2418
2701
  const upsertDocs = chunks.map((chunk, i) => ({
2419
2702
  id: chunk.id,
@@ -2440,10 +2723,7 @@ var Pipeline = class {
2440
2723
  });
2441
2724
  } catch (error) {
2442
2725
  console.error(`[Pipeline] Failed to ingest document ${doc.docId}:`, error);
2443
- results.push({
2444
- docId: doc.docId,
2445
- chunksIngested: 0
2446
- });
2726
+ results.push({ docId: doc.docId, chunksIngested: 0 });
2447
2727
  }
2448
2728
  }
2449
2729
  return results;
@@ -2473,7 +2753,6 @@ ${m.content}`).join("\n\n---\n\n") : "No relevant context found.";
2473
2753
  var ProviderHealthCheck = class {
2474
2754
  /**
2475
2755
  * Validates vector database configuration before initialization.
2476
- * Performs connectivity checks and verifies required resources exist.
2477
2756
  */
2478
2757
  static async checkVectorProvider(config) {
2479
2758
  const timestamp = Date.now();
@@ -2481,7 +2760,6 @@ var ProviderHealthCheck = class {
2481
2760
  projectId: "health-check",
2482
2761
  vectorDb: config,
2483
2762
  llm: { provider: "openai", model: "gpt-4o" },
2484
- // dummy
2485
2763
  embedding: { provider: "openai", model: "text-embedding-3-small" }
2486
2764
  });
2487
2765
  const vectorDbErrors = configErrors.filter((e) => e.field.startsWith("vectorDb"));
@@ -2551,10 +2829,7 @@ var ProviderHealthCheck = class {
2551
2829
  return {
2552
2830
  healthy: true,
2553
2831
  provider: "pinecone",
2554
- capabilities: {
2555
- indexes: indexNames.length,
2556
- targetIndex: config.indexName
2557
- },
2832
+ capabilities: { indexes: indexNames.length, targetIndex: config.indexName },
2558
2833
  timestamp
2559
2834
  };
2560
2835
  } catch (error) {
@@ -2573,9 +2848,7 @@ var ProviderHealthCheck = class {
2573
2848
  const client = new Client({ connectionString: opts.connectionString });
2574
2849
  await client.connect();
2575
2850
  const result = await client.query(`
2576
- SELECT EXISTS(
2577
- SELECT 1 FROM pg_extension WHERE extname = 'vector'
2578
- );
2851
+ SELECT EXISTS(SELECT 1 FROM pg_extension WHERE extname = 'vector');
2579
2852
  `);
2580
2853
  const hasVector = result.rows[0].exists;
2581
2854
  await client.end();
@@ -2610,7 +2883,7 @@ var ProviderHealthCheck = class {
2610
2883
  provider: "mongodb",
2611
2884
  capabilities: {
2612
2885
  collections: collectionNames.length,
2613
- targetCollection: hasCollection ? opts.collection : "NOT FOUND"
2886
+ targetCollection: hasCollection ? opts.collection : "NOT FOUND (will be created on first insert)"
2614
2887
  },
2615
2888
  timestamp
2616
2889
  };
@@ -2623,16 +2896,16 @@ var ProviderHealthCheck = class {
2623
2896
  };
2624
2897
  }
2625
2898
  }
2899
+ /**
2900
+ * Milvus health check — uses baseUrl/uri (matching MilvusProvider constructor).
2901
+ */
2626
2902
  static async checkMilvus(config, timestamp) {
2627
2903
  const opts = config.options;
2628
- const host = opts.host || "localhost";
2629
- const port = opts.port || 19530;
2904
+ const baseUrl = opts.baseUrl || opts.uri || (opts.host ? `http://${opts.host}:${opts.port || 19530}` : "http://localhost:19530");
2630
2905
  try {
2631
2906
  const controller = new AbortController();
2632
2907
  const timeoutId = setTimeout(() => controller.abort(), 5e3);
2633
- const response = await fetch(`http://${host}:${port}/healthz`, {
2634
- signal: controller.signal
2635
- });
2908
+ const response = await fetch(`${baseUrl}/v1/health`, { signal: controller.signal });
2636
2909
  clearTimeout(timeoutId);
2637
2910
  if (!response.ok) {
2638
2911
  throw new Error(`Health check returned ${response.status}`);
@@ -2640,7 +2913,7 @@ var ProviderHealthCheck = class {
2640
2913
  return {
2641
2914
  healthy: true,
2642
2915
  provider: "milvus",
2643
- capabilities: { endpoint: `${host}:${port}` },
2916
+ capabilities: { endpoint: baseUrl },
2644
2917
  timestamp
2645
2918
  };
2646
2919
  } catch (error) {
@@ -2652,17 +2925,18 @@ var ProviderHealthCheck = class {
2652
2925
  };
2653
2926
  }
2654
2927
  }
2928
+ /**
2929
+ * Qdrant health check — uses baseUrl (matching QdrantProvider constructor).
2930
+ */
2655
2931
  static async checkQdrant(config, timestamp) {
2656
2932
  const opts = config.options;
2657
- const baseUrl = opts.baseUrl || opts.url || "http://localhost:6333";
2933
+ const baseUrl = (opts.baseUrl || opts.url || "http://localhost:6333").replace(/\/$/, "");
2658
2934
  try {
2659
2935
  const apiKey = opts.apiKey;
2660
2936
  const response = await fetch(`${baseUrl}/`, {
2661
2937
  headers: apiKey ? { "api-key": apiKey } : {}
2662
2938
  });
2663
- if (!response.ok) {
2664
- throw new Error(`Health check returned ${response.status}`);
2665
- }
2939
+ if (!response.ok) throw new Error(`Health check returned ${response.status}`);
2666
2940
  const health = await response.json();
2667
2941
  return {
2668
2942
  healthy: true,
@@ -2679,19 +2953,19 @@ var ProviderHealthCheck = class {
2679
2953
  };
2680
2954
  }
2681
2955
  }
2956
+ /**
2957
+ * ChromaDB health check — uses baseUrl/host (matching ChromaDBProvider constructor).
2958
+ */
2682
2959
  static async checkChromaDB(config, timestamp) {
2683
2960
  const opts = config.options;
2684
- const host = opts.host || "localhost";
2685
- const port = opts.port || 8e3;
2961
+ const baseUrl = opts.baseUrl || (opts.host ? `http://${opts.host}:${opts.port || 8e3}` : "http://localhost:8000");
2686
2962
  try {
2687
- const response = await fetch(`http://${host}:${port}/api/v1/heartbeat`);
2688
- if (!response.ok) {
2689
- throw new Error(`Health check returned ${response.status}`);
2690
- }
2963
+ const response = await fetch(`${baseUrl}/api/v1/heartbeat`);
2964
+ if (!response.ok) throw new Error(`Health check returned ${response.status}`);
2691
2965
  return {
2692
2966
  healthy: true,
2693
2967
  provider: "chromadb",
2694
- capabilities: { endpoint: `${host}:${port}` },
2968
+ capabilities: { endpoint: baseUrl },
2695
2969
  timestamp
2696
2970
  };
2697
2971
  } catch (error) {
@@ -2703,38 +2977,43 @@ var ProviderHealthCheck = class {
2703
2977
  };
2704
2978
  }
2705
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
+ */
2706
2984
  static async checkRedis(config, timestamp) {
2707
2985
  const opts = config.options;
2708
- const url = opts.url || `redis://${opts.host}:${opts.port}`;
2709
- try {
2710
- await fetch(url);
2711
- return {
2712
- healthy: true,
2713
- provider: "redis",
2714
- capabilities: { endpoint: url },
2715
- timestamp
2716
- };
2717
- } catch (error) {
2718
- return {
2719
- healthy: false,
2720
- provider: "redis",
2721
- error: `Connection check failed: ${error instanceof Error ? error.message : String(error)}. Ensure Redis is running at ${url}`,
2722
- timestamp
2723
- };
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
+ }
2724
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
+ };
2725
3003
  }
3004
+ /**
3005
+ * Weaviate health check — uses baseUrl/url (matching WeaviateProvider constructor).
3006
+ */
2726
3007
  static async checkWeaviate(config, timestamp) {
2727
3008
  const opts = config.options;
2728
- const url = (opts.url || "http://localhost:8080").replace(/\/$/, "");
3009
+ const baseUrl = (opts.baseUrl || opts.url || "http://localhost:8080").replace(/\/$/, "");
2729
3010
  try {
2730
- const response = await fetch(`${url}/.well-known/ready`);
2731
- if (!response.ok) {
2732
- throw new Error(`Health check returned ${response.status}`);
2733
- }
3011
+ const response = await fetch(`${baseUrl}/v1/.well-known/ready`);
3012
+ if (!response.ok) throw new Error(`Health check returned ${response.status}`);
2734
3013
  return {
2735
3014
  healthy: true,
2736
3015
  provider: "weaviate",
2737
- capabilities: { endpoint: url },
3016
+ capabilities: { endpoint: baseUrl },
2738
3017
  timestamp
2739
3018
  };
2740
3019
  } catch (error) {
@@ -2750,12 +3029,7 @@ var ProviderHealthCheck = class {
2750
3029
  const opts = config.options;
2751
3030
  const baseUrl = (opts.baseUrl || "").replace(/\/$/, "");
2752
3031
  if (!baseUrl) {
2753
- return {
2754
- healthy: false,
2755
- provider: "rest",
2756
- error: "baseUrl is required",
2757
- timestamp
2758
- };
3032
+ return { healthy: false, provider: "rest", error: "baseUrl is required", timestamp };
2759
3033
  }
2760
3034
  try {
2761
3035
  const response = await fetch(`${baseUrl}/health`, {
@@ -2818,11 +3092,7 @@ var ProviderHealthCheck = class {
2818
3092
  return {
2819
3093
  healthy: true,
2820
3094
  provider: "openai",
2821
- capabilities: {
2822
- model: config.model,
2823
- available: hasModel,
2824
- totalModels: models.data.length
2825
- },
3095
+ capabilities: { model: config.model, available: hasModel, totalModels: models.data.length },
2826
3096
  timestamp
2827
3097
  };
2828
3098
  } catch (error) {
@@ -2843,12 +3113,7 @@ var ProviderHealthCheck = class {
2843
3113
  max_tokens: 10,
2844
3114
  messages: [{ role: "user", content: "ping" }]
2845
3115
  });
2846
- return {
2847
- healthy: true,
2848
- provider: "anthropic",
2849
- capabilities: { model: config.model },
2850
- timestamp
2851
- };
3116
+ return { healthy: true, provider: "anthropic", capabilities: { model: config.model }, timestamp };
2852
3117
  } catch (error) {
2853
3118
  return {
2854
3119
  healthy: false,
@@ -2862,20 +3127,14 @@ var ProviderHealthCheck = class {
2862
3127
  const baseUrl = (config.baseUrl || "http://localhost:11434").replace(/\/$/, "");
2863
3128
  try {
2864
3129
  const response = await fetch(`${baseUrl}/api/tags`);
2865
- if (!response.ok) {
2866
- throw new Error(`Health check returned ${response.status}`);
2867
- }
3130
+ if (!response.ok) throw new Error(`Health check returned ${response.status}`);
2868
3131
  const data = await response.json();
2869
3132
  const models = data.models || [];
2870
3133
  const hasModel = models.some((m) => m.name === config.model);
2871
3134
  return {
2872
3135
  healthy: true,
2873
3136
  provider: "ollama",
2874
- capabilities: {
2875
- model: config.model,
2876
- available: hasModel,
2877
- totalModels: models.length
2878
- },
3137
+ capabilities: { model: config.model, available: hasModel, totalModels: models.length },
2879
3138
  timestamp
2880
3139
  };
2881
3140
  } catch (error) {
@@ -2892,18 +3151,11 @@ var ProviderHealthCheck = class {
2892
3151
  const baseUrlRaw = config.baseUrl || ((_a = config.options) == null ? void 0 : _a.baseUrl);
2893
3152
  const baseUrl = (typeof baseUrlRaw === "string" ? baseUrlRaw : "").replace(/\/$/, "");
2894
3153
  if (!baseUrl) {
2895
- return {
2896
- healthy: false,
2897
- provider: config.provider,
2898
- error: "baseUrl is required",
2899
- timestamp
2900
- };
3154
+ return { healthy: false, provider: config.provider, error: "baseUrl is required", timestamp };
2901
3155
  }
2902
3156
  try {
2903
3157
  const headers = (_b = config.options) == null ? void 0 : _b.headers;
2904
- const response = await fetch(`${baseUrl}/health`, {
2905
- headers: headers || void 0
2906
- });
3158
+ const response = await fetch(`${baseUrl}/health`, { headers: headers || void 0 });
2907
3159
  return {
2908
3160
  healthy: response.ok,
2909
3161
  provider: config.provider,
@@ -2920,7 +3172,7 @@ var ProviderHealthCheck = class {
2920
3172
  }
2921
3173
  }
2922
3174
  /**
2923
- * Runs comprehensive health checks on all configured providers.
3175
+ * Runs comprehensive health checks on all configured providers in parallel.
2924
3176
  */
2925
3177
  static async checkAll(vectorDbConfig, llmConfig, embeddingConfig) {
2926
3178
  const [vectorDb, llm, embedding] = await Promise.all([