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