@retrivora-ai/rag-engine 0.1.5 → 0.1.7

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 (41) hide show
  1. package/dist/{DocumentChunker-cfaMidtA.d.mts → DocumentChunker-BEyzadsv.d.mts} +2 -2
  2. package/dist/{DocumentChunker-cfaMidtA.d.ts → DocumentChunker-BEyzadsv.d.ts} +2 -2
  3. package/dist/{PineconeProvider-NJ675H7U.mjs → PineconeProvider-ZRAFNFEC.mjs} +1 -1
  4. package/dist/{PostgreSQLProvider-ISNMD3BE.mjs → PostgreSQLProvider-ZNXA67IM.mjs} +1 -1
  5. package/dist/{QdrantProvider-LJWOIGES.mjs → QdrantProvider-VAED5VA7.mjs} +1 -1
  6. package/dist/{RagConfig-DG_0f8ka.d.mts → RagConfig-hBGXJmSx.d.mts} +3 -3
  7. package/dist/{RagConfig-DG_0f8ka.d.ts → RagConfig-hBGXJmSx.d.ts} +3 -3
  8. package/dist/chunk-7YQWGERZ.mjs +1764 -0
  9. package/dist/chunk-CWQQHAF6.mjs +157 -0
  10. package/dist/{chunk-S5DRHETN.mjs → chunk-IUTAZ7QR.mjs} +31 -2
  11. package/dist/{chunk-AALIF3AL.mjs → chunk-ZM6TYIDH.mjs} +3 -3
  12. package/dist/handlers/index.d.mts +3 -44
  13. package/dist/handlers/index.d.ts +3 -44
  14. package/dist/handlers/index.js +1371 -60
  15. package/dist/handlers/index.mjs +1 -1
  16. package/dist/index-Bx182KKn.d.ts +64 -0
  17. package/dist/index-Ck2pt7-8.d.mts +64 -0
  18. package/dist/index.d.mts +4 -4
  19. package/dist/index.d.ts +4 -4
  20. package/dist/server.d.mts +74 -18
  21. package/dist/server.d.ts +74 -18
  22. package/dist/server.js +1371 -60
  23. package/dist/server.mjs +4 -4
  24. package/package.json +2 -1
  25. package/src/config/serverConfig.ts +4 -0
  26. package/src/core/BatchProcessor.ts +347 -0
  27. package/src/core/ConfigValidator.ts +568 -0
  28. package/src/core/Pipeline.ts +89 -39
  29. package/src/core/ProviderHealthCheck.ts +568 -0
  30. package/src/core/VectorPlugin.ts +49 -8
  31. package/src/handlers/index.ts +2 -2
  32. package/src/providers/vectordb/BaseVectorProvider.ts +1 -1
  33. package/src/providers/vectordb/MilvusProvider.ts +1 -1
  34. package/src/providers/vectordb/MongoDBProvider.ts +1 -1
  35. package/src/providers/vectordb/PineconeProvider.ts +4 -4
  36. package/src/providers/vectordb/PostgreSQLProvider.ts +35 -3
  37. package/src/providers/vectordb/QdrantProvider.ts +81 -4
  38. package/src/rag/DocumentChunker.ts +2 -2
  39. package/src/types/index.ts +3 -3
  40. package/dist/chunk-6FODXNUF.mjs +0 -91
  41. package/dist/chunk-BP4U4TT5.mjs +0 -548
@@ -0,0 +1,1764 @@
1
+ import {
2
+ LLMFactory
3
+ } from "./chunk-JI6VD5TJ.mjs";
4
+ import {
5
+ mergeDefined
6
+ } from "./chunk-UKDXCXW7.mjs";
7
+ import {
8
+ __spreadProps,
9
+ __spreadValues
10
+ } from "./chunk-I4E63NIC.mjs";
11
+
12
+ // src/handlers/index.ts
13
+ import { NextResponse } from "next/server";
14
+
15
+ // src/config/serverConfig.ts
16
+ var VECTOR_DB_PROVIDERS = ["pinecone", "pgvector", "postgresql", "mongodb", "milvus", "qdrant", "chromadb", "redis", "weaviate", "rest", "universal_rest", "custom"];
17
+ var LLM_PROVIDERS = ["openai", "anthropic", "ollama", "rest", "universal_rest", "custom"];
18
+ var EMBEDDING_PROVIDERS = ["openai", "ollama", "rest", "universal_rest", "custom"];
19
+ function readString(env, name) {
20
+ var _a;
21
+ const value = (_a = env[name]) == null ? void 0 : _a.trim();
22
+ return value ? value : void 0;
23
+ }
24
+ function readNumber(env, name, fallback) {
25
+ const value = env[name];
26
+ if (!value) return fallback;
27
+ const parsed = Number(value);
28
+ if (!Number.isFinite(parsed)) {
29
+ throw new Error(`[getRagConfig] ${name} must be a valid number`);
30
+ }
31
+ return parsed;
32
+ }
33
+ function readEnum(env, name, fallback, allowed) {
34
+ var _a;
35
+ const value = (_a = readString(env, name)) != null ? _a : fallback;
36
+ if (allowed.includes(value)) {
37
+ return value;
38
+ }
39
+ throw new Error(`[getRagConfig] ${name} must be one of: ${allowed.join(", ")}`);
40
+ }
41
+ 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;
43
+ const projectId = (_b = (_a = readString(env, "RAG_PROJECT_ID")) != null ? _a : readString(env, "NEXT_PUBLIC_PROJECT_ID")) != null ? _b : "__default__";
44
+ const vectorProvider = readEnum(env, "VECTOR_DB_PROVIDER", "pinecone", VECTOR_DB_PROVIDERS);
45
+ const llmProvider = readEnum(env, "LLM_PROVIDER", "openai", LLM_PROVIDERS);
46
+ const embeddingProvider = readEnum(env, "EMBEDDING_PROVIDER", "openai", EMBEDDING_PROVIDERS);
47
+ const embeddingDimensions = readNumber(env, "EMBEDDING_DIMENSIONS", 1536);
48
+ const vectorDbOptions = {};
49
+ if (vectorProvider === "pinecone") {
50
+ 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 : "";
54
+ vectorDbOptions.dimensions = embeddingDimensions;
55
+ } else if (vectorProvider === "rest") {
56
+ vectorDbOptions.baseUrl = (_f = readString(env, "VECTOR_DB_REST_URL")) != null ? _f : "";
57
+ vectorDbOptions.headers = readString(env, "VECTOR_DB_REST_API_KEY") ? { "api-key": readString(env, "VECTOR_DB_REST_API_KEY") } : {};
58
+ } 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 : "";
60
+ vectorDbOptions.profile = readString(env, "VECTOR_UNIVERSAL_PROFILE");
61
+ vectorDbOptions.headers = readString(env, "VECTOR_DB_REST_API_KEY") ? { Authorization: `Bearer ${readString(env, "VECTOR_DB_REST_API_KEY")}` } : {};
62
+ } else if (vectorProvider === "mongodb") {
63
+ vectorDbOptions.uri = (_i = readString(env, "MONGODB_URI")) != null ? _i : "";
64
+ vectorDbOptions.database = (_j = readString(env, "MONGODB_DB")) != null ? _j : "";
65
+ vectorDbOptions.collection = (_k = readString(env, "MONGODB_COLLECTION")) != null ? _k : "";
66
+ vectorDbOptions.indexName = (_l = readString(env, "MONGODB_INDEX_NAME")) != null ? _l : "vector_index";
67
+ } else if (vectorProvider === "qdrant") {
68
+ vectorDbOptions.baseUrl = (_m = readString(env, "QDRANT_URL")) != null ? _m : "http://localhost:6333";
69
+ vectorDbOptions.apiKey = readString(env, "QDRANT_API_KEY");
70
+ vectorDbOptions.dimensions = embeddingDimensions;
71
+ }
72
+ const llmApiKeyByProvider = {
73
+ openai: readString(env, "OPENAI_API_KEY"),
74
+ anthropic: readString(env, "ANTHROPIC_API_KEY"),
75
+ ollama: void 0,
76
+ universal_rest: readString(env, "LLM_API_KEY")
77
+ };
78
+ const embeddingApiKeyByProvider = {
79
+ openai: readString(env, "OPENAI_API_KEY"),
80
+ ollama: void 0,
81
+ custom: (_n = readString(env, "EMBEDDING_API_KEY")) != null ? _n : readString(env, "OPENAI_API_KEY")
82
+ };
83
+ return {
84
+ projectId,
85
+ vectorDb: {
86
+ provider: vectorProvider,
87
+ indexName: (_o = readString(env, "VECTOR_DB_INDEX")) != null ? _o : "rag-index",
88
+ options: vectorDbOptions
89
+ },
90
+ llm: {
91
+ provider: llmProvider,
92
+ model: (_p = readString(env, "LLM_MODEL")) != null ? _p : "gpt-4o",
93
+ apiKey: (_q = llmApiKeyByProvider[llmProvider]) != null ? _q : "",
94
+ baseUrl: readString(env, "LLM_BASE_URL"),
95
+ systemPrompt: readString(env, "LLM_SYSTEM_PROMPT"),
96
+ maxTokens: readNumber(env, "LLM_MAX_TOKENS", 1024),
97
+ temperature: readNumber(env, "LLM_TEMPERATURE", 0.7),
98
+ options: {
99
+ profile: readString(env, "LLM_UNIVERSAL_PROFILE")
100
+ }
101
+ },
102
+ embedding: {
103
+ provider: embeddingProvider,
104
+ model: (_r = readString(env, "EMBEDDING_MODEL")) != null ? _r : "text-embedding-3-small",
105
+ apiKey: embeddingApiKeyByProvider[embeddingProvider],
106
+ baseUrl: readString(env, "EMBEDDING_BASE_URL"),
107
+ dimensions: embeddingDimensions,
108
+ options: {
109
+ profile: readString(env, "EMBEDDING_UNIVERSAL_PROFILE")
110
+ }
111
+ },
112
+ ui: {
113
+ title: (_t = (_s = readString(env, "NEXT_PUBLIC_UI_TITLE")) != null ? _s : readString(env, "UI_TITLE")) != null ? _t : "AI Assistant",
114
+ subtitle: (_v = (_u = readString(env, "NEXT_PUBLIC_UI_SUBTITLE")) != null ? _u : readString(env, "UI_SUBTITLE")) != null ? _v : "Powered by RAG",
115
+ primaryColor: (_x = (_w = readString(env, "NEXT_PUBLIC_PRIMARY_COLOR")) != null ? _w : readString(env, "UI_PRIMARY_COLOR")) != null ? _x : "#10b981",
116
+ accentColor: (_z = (_y = readString(env, "NEXT_PUBLIC_ACCENT_COLOR")) != null ? _y : readString(env, "UI_ACCENT_COLOR")) != null ? _z : "#3b82f6",
117
+ logoUrl: (_A = readString(env, "NEXT_PUBLIC_LOGO_URL")) != null ? _A : readString(env, "UI_LOGO_URL"),
118
+ placeholder: (_C = (_B = readString(env, "NEXT_PUBLIC_PLACEHOLDER")) != null ? _B : readString(env, "UI_PLACEHOLDER")) != null ? _C : "Ask me anything\u2026",
119
+ showSources: ((_E = (_D = readString(env, "NEXT_PUBLIC_SHOW_SOURCES")) != null ? _D : readString(env, "UI_SHOW_SOURCES")) != null ? _E : "true") !== "false",
120
+ 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.",
121
+ visualStyle: (_I = (_H = readString(env, "NEXT_PUBLIC_UI_VISUAL_STYLE")) != null ? _H : readString(env, "UI_VISUAL_STYLE")) != null ? _I : "glass",
122
+ borderRadius: (_K = (_J = readString(env, "NEXT_PUBLIC_UI_BORDER_RADIUS")) != null ? _J : readString(env, "UI_BORDER_RADIUS")) != null ? _K : "xl"
123
+ },
124
+ rag: {
125
+ topK: readNumber(env, "RAG_TOP_K", 5),
126
+ scoreThreshold: readNumber(env, "RAG_SCORE_THRESHOLD", 0),
127
+ chunkSize: readNumber(env, "RAG_CHUNK_SIZE", 1e3),
128
+ chunkOverlap: readNumber(env, "RAG_CHUNK_OVERLAP", 200)
129
+ }
130
+ };
131
+ }
132
+
133
+ // src/core/ConfigResolver.ts
134
+ var ConfigResolver = class {
135
+ /**
136
+ * Resolves the final configuration by merging host-provided config with defaults.
137
+ * @param hostConfig - Partial configuration passed from the host application.
138
+ */
139
+ static resolve(hostConfig) {
140
+ const envConfig = getRagConfig();
141
+ if (!hostConfig) {
142
+ return envConfig;
143
+ }
144
+ return __spreadProps(__spreadValues(__spreadValues({}, envConfig), hostConfig), {
145
+ projectId: hostConfig.projectId || envConfig.projectId,
146
+ vectorDb: hostConfig.vectorDb ? __spreadProps(__spreadValues(__spreadValues({}, envConfig.vectorDb), hostConfig.vectorDb), {
147
+ options: __spreadValues(__spreadValues({}, envConfig.vectorDb.options), hostConfig.vectorDb.options || {})
148
+ }) : envConfig.vectorDb,
149
+ llm: hostConfig.llm ? __spreadProps(__spreadValues(__spreadValues({}, envConfig.llm), hostConfig.llm), {
150
+ options: __spreadValues(__spreadValues({}, envConfig.llm.options || {}), hostConfig.llm.options || {})
151
+ }) : envConfig.llm,
152
+ embedding: hostConfig.embedding ? __spreadProps(__spreadValues(__spreadValues({}, envConfig.embedding), hostConfig.embedding), {
153
+ options: __spreadValues(__spreadValues({}, envConfig.embedding.options || {}), hostConfig.embedding.options || {})
154
+ }) : envConfig.embedding,
155
+ ui: hostConfig.ui ? mergeDefined(envConfig.ui, hostConfig.ui) : envConfig.ui,
156
+ rag: hostConfig.rag ? __spreadValues(__spreadValues({}, envConfig.rag), hostConfig.rag) : envConfig.rag
157
+ });
158
+ }
159
+ /**
160
+ * Validates the configuration for required fields.
161
+ */
162
+ static validate(config) {
163
+ if (!config.projectId) {
164
+ throw new Error("[ConfigResolver] projectId is required");
165
+ }
166
+ if (!config.vectorDb.provider) {
167
+ throw new Error("[ConfigResolver] vectorDb.provider is required");
168
+ }
169
+ if (!config.llm.provider) {
170
+ throw new Error("[ConfigResolver] llm.provider is required");
171
+ }
172
+ }
173
+ };
174
+
175
+ // src/core/ConfigValidator.ts
176
+ var ConfigValidator = class {
177
+ /**
178
+ * Validates the entire RagConfig object.
179
+ * Returns an array of validation errors. Empty array = valid config.
180
+ */
181
+ static validate(config) {
182
+ const errors = [];
183
+ if (!config.projectId) {
184
+ errors.push({
185
+ field: "projectId",
186
+ message: "projectId is required",
187
+ severity: "error"
188
+ });
189
+ }
190
+ errors.push(...this.validateVectorDbConfig(config.vectorDb));
191
+ errors.push(...this.validateLLMConfig(config.llm));
192
+ if (config.embedding) {
193
+ errors.push(...this.validateEmbeddingConfig(config.embedding));
194
+ } else if (config.llm.provider === "anthropic") {
195
+ errors.push({
196
+ field: "embedding",
197
+ message: "Embedding config is required when using Anthropic LLM",
198
+ suggestion: 'Provide embedding config with provider (e.g., "openai")',
199
+ severity: "error"
200
+ });
201
+ }
202
+ if (config.ui) {
203
+ errors.push(...this.validateUIConfig(config.ui));
204
+ }
205
+ if (config.rag) {
206
+ errors.push(...this.validateRAGConfig(config.rag));
207
+ }
208
+ return errors;
209
+ }
210
+ static validateVectorDbConfig(config) {
211
+ const errors = [];
212
+ if (!config.provider) {
213
+ errors.push({
214
+ field: "vectorDb.provider",
215
+ message: "Vector database provider is required",
216
+ severity: "error"
217
+ });
218
+ return errors;
219
+ }
220
+ if (!config.indexName) {
221
+ errors.push({
222
+ field: "vectorDb.indexName",
223
+ message: "Vector database index name is required",
224
+ severity: "error"
225
+ });
226
+ }
227
+ switch (config.provider) {
228
+ case "pinecone":
229
+ errors.push(...this.validatePineconeConfig(config));
230
+ break;
231
+ case "pgvector":
232
+ case "postgresql":
233
+ errors.push(...this.validatePostgresConfig(config));
234
+ break;
235
+ case "mongodb":
236
+ errors.push(...this.validateMongoDBConfig(config));
237
+ break;
238
+ case "milvus":
239
+ errors.push(...this.validateMilvusConfig(config));
240
+ break;
241
+ case "qdrant":
242
+ errors.push(...this.validateQdrantConfig(config));
243
+ break;
244
+ case "chromadb":
245
+ errors.push(...this.validateChromaDBConfig(config));
246
+ break;
247
+ case "redis":
248
+ errors.push(...this.validateRedisConfig(config));
249
+ break;
250
+ case "weaviate":
251
+ errors.push(...this.validateWeaviateConfig(config));
252
+ break;
253
+ case "universal_rest":
254
+ case "rest":
255
+ errors.push(...this.validateRestConfig(config));
256
+ break;
257
+ }
258
+ return errors;
259
+ }
260
+ static validatePineconeConfig(config) {
261
+ const errors = [];
262
+ const opts = config.options;
263
+ if (!opts.apiKey) {
264
+ errors.push({
265
+ field: "vectorDb.options.apiKey",
266
+ message: "Pinecone API key is required",
267
+ suggestion: "Set PINECONE_API_KEY environment variable",
268
+ severity: "error"
269
+ });
270
+ }
271
+ if (!opts.environment) {
272
+ errors.push({
273
+ field: "vectorDb.options.environment",
274
+ message: "Pinecone environment is required",
275
+ suggestion: 'Set PINECONE_ENVIRONMENT environment variable (e.g., "gcp-starter")',
276
+ severity: "error"
277
+ });
278
+ }
279
+ return errors;
280
+ }
281
+ static validatePostgresConfig(config) {
282
+ const errors = [];
283
+ const opts = config.options;
284
+ if (!opts.connectionString) {
285
+ errors.push({
286
+ field: "vectorDb.options.connectionString",
287
+ message: "PostgreSQL connection string is required",
288
+ suggestion: "Set PGVECTOR_CONNECTION_STRING environment variable",
289
+ severity: "error"
290
+ });
291
+ }
292
+ return errors;
293
+ }
294
+ static validateMongoDBConfig(config) {
295
+ const errors = [];
296
+ const opts = config.options;
297
+ if (!opts.uri) {
298
+ errors.push({
299
+ field: "vectorDb.options.uri",
300
+ message: "MongoDB connection URI is required",
301
+ suggestion: "Set MONGODB_URI environment variable",
302
+ severity: "error"
303
+ });
304
+ }
305
+ if (!opts.database) {
306
+ errors.push({
307
+ field: "vectorDb.options.database",
308
+ message: "MongoDB database name is required",
309
+ suggestion: "Set MONGODB_DB environment variable",
310
+ severity: "error"
311
+ });
312
+ }
313
+ if (!opts.collection) {
314
+ errors.push({
315
+ field: "vectorDb.options.collection",
316
+ message: "MongoDB collection name is required",
317
+ suggestion: "Set MONGODB_COLLECTION environment variable",
318
+ severity: "error"
319
+ });
320
+ }
321
+ return errors;
322
+ }
323
+ static validateMilvusConfig(config) {
324
+ const errors = [];
325
+ const opts = config.options;
326
+ if (!opts.host) {
327
+ errors.push({
328
+ field: "vectorDb.options.host",
329
+ message: "Milvus host is required",
330
+ suggestion: "Set MILVUS_HOST environment variable",
331
+ severity: "error"
332
+ });
333
+ }
334
+ if (!opts.port) {
335
+ errors.push({
336
+ field: "vectorDb.options.port",
337
+ message: "Milvus port is required",
338
+ suggestion: "Set MILVUS_PORT environment variable (default: 19530)",
339
+ severity: "error"
340
+ });
341
+ }
342
+ return errors;
343
+ }
344
+ static validateQdrantConfig(config) {
345
+ const errors = [];
346
+ const opts = config.options;
347
+ if (!opts.baseUrl && !opts.url) {
348
+ errors.push({
349
+ field: "vectorDb.options.baseUrl",
350
+ message: "Qdrant base URL is required",
351
+ suggestion: 'Set QDRANT_URL environment variable (e.g., "http://localhost:6333")',
352
+ severity: "error"
353
+ });
354
+ }
355
+ return errors;
356
+ }
357
+ static validateChromaDBConfig(config) {
358
+ const errors = [];
359
+ const opts = config.options;
360
+ if (!opts.host && !opts.path) {
361
+ errors.push({
362
+ field: "vectorDb.options.host",
363
+ message: "ChromaDB host or path is required",
364
+ suggestion: "Set CHROMADB_HOST or CHROMADB_PATH environment variable",
365
+ severity: "error"
366
+ });
367
+ }
368
+ return errors;
369
+ }
370
+ static validateRedisConfig(config) {
371
+ const errors = [];
372
+ const opts = config.options;
373
+ if (!opts.url && (!opts.host || !opts.port)) {
374
+ errors.push({
375
+ field: "vectorDb.options.url",
376
+ message: "Redis connection URL or host:port is required",
377
+ suggestion: "Set REDIS_URL or REDIS_HOST/REDIS_PORT environment variables",
378
+ severity: "error"
379
+ });
380
+ }
381
+ return errors;
382
+ }
383
+ static validateWeaviateConfig(config) {
384
+ const errors = [];
385
+ const opts = config.options;
386
+ if (!opts.url) {
387
+ errors.push({
388
+ field: "vectorDb.options.url",
389
+ message: "Weaviate instance URL is required",
390
+ suggestion: "Set WEAVIATE_URL environment variable",
391
+ severity: "error"
392
+ });
393
+ }
394
+ return errors;
395
+ }
396
+ static validateRestConfig(config) {
397
+ const errors = [];
398
+ const opts = config.options;
399
+ if (!opts.baseUrl) {
400
+ errors.push({
401
+ field: "vectorDb.options.baseUrl",
402
+ message: "REST API base URL is required",
403
+ suggestion: "Set VECTOR_BASE_URL environment variable",
404
+ severity: "error"
405
+ });
406
+ }
407
+ return errors;
408
+ }
409
+ static validateLLMConfig(config) {
410
+ var _a;
411
+ const errors = [];
412
+ if (!config.provider) {
413
+ errors.push({
414
+ field: "llm.provider",
415
+ message: "LLM provider is required",
416
+ severity: "error"
417
+ });
418
+ return errors;
419
+ }
420
+ if (!config.model) {
421
+ errors.push({
422
+ field: "llm.model",
423
+ message: "LLM model name is required",
424
+ suggestion: `e.g., "gpt-4o" for OpenAI, "claude-3-opus" for Anthropic`,
425
+ severity: "error"
426
+ });
427
+ }
428
+ switch (config.provider) {
429
+ case "openai":
430
+ if (!config.apiKey) {
431
+ errors.push({
432
+ field: "llm.apiKey",
433
+ message: "OpenAI API key is required",
434
+ suggestion: "Set OPENAI_API_KEY environment variable",
435
+ severity: "error"
436
+ });
437
+ }
438
+ break;
439
+ case "anthropic":
440
+ if (!config.apiKey) {
441
+ errors.push({
442
+ field: "llm.apiKey",
443
+ message: "Anthropic API key is required",
444
+ suggestion: "Set ANTHROPIC_API_KEY environment variable",
445
+ severity: "error"
446
+ });
447
+ }
448
+ break;
449
+ case "ollama":
450
+ if (!config.baseUrl) {
451
+ errors.push({
452
+ field: "llm.baseUrl",
453
+ message: "Ollama base URL is required",
454
+ suggestion: 'Set LLM_BASE_URL environment variable (e.g., "http://localhost:11434")',
455
+ severity: "error"
456
+ });
457
+ }
458
+ break;
459
+ case "rest":
460
+ case "universal_rest":
461
+ if (!config.baseUrl && !((_a = config.options) == null ? void 0 : _a.baseUrl)) {
462
+ errors.push({
463
+ field: "llm.baseUrl",
464
+ message: "REST API base URL is required",
465
+ suggestion: "Set LLM_BASE_URL environment variable",
466
+ severity: "error"
467
+ });
468
+ }
469
+ break;
470
+ }
471
+ if (config.temperature !== void 0) {
472
+ if (config.temperature < 0 || config.temperature > 2) {
473
+ errors.push({
474
+ field: "llm.temperature",
475
+ message: "Temperature must be between 0 and 2",
476
+ severity: "error"
477
+ });
478
+ }
479
+ }
480
+ if (config.maxTokens !== void 0 && config.maxTokens <= 0) {
481
+ errors.push({
482
+ field: "llm.maxTokens",
483
+ message: "maxTokens must be greater than 0",
484
+ severity: "error"
485
+ });
486
+ }
487
+ return errors;
488
+ }
489
+ static validateEmbeddingConfig(config) {
490
+ const errors = [];
491
+ if (!config.provider) {
492
+ errors.push({
493
+ field: "embedding.provider",
494
+ message: "Embedding provider is required",
495
+ severity: "error"
496
+ });
497
+ return errors;
498
+ }
499
+ if (!config.model) {
500
+ errors.push({
501
+ field: "embedding.model",
502
+ message: "Embedding model name is required",
503
+ suggestion: 'e.g., "text-embedding-3-small" for OpenAI',
504
+ severity: "error"
505
+ });
506
+ }
507
+ if (config.provider === "openai" && !config.apiKey) {
508
+ errors.push({
509
+ field: "embedding.apiKey",
510
+ message: "OpenAI API key is required for embedding",
511
+ suggestion: "Set OPENAI_API_KEY environment variable",
512
+ severity: "error"
513
+ });
514
+ }
515
+ if (config.provider === "ollama" && !config.baseUrl) {
516
+ errors.push({
517
+ field: "embedding.baseUrl",
518
+ message: "Ollama base URL is required",
519
+ suggestion: "Set EMBEDDING_BASE_URL environment variable",
520
+ severity: "error"
521
+ });
522
+ }
523
+ if (config.dimensions !== void 0 && config.dimensions <= 0) {
524
+ errors.push({
525
+ field: "embedding.dimensions",
526
+ message: "Embedding dimensions must be greater than 0",
527
+ severity: "error"
528
+ });
529
+ }
530
+ return errors;
531
+ }
532
+ static validateUIConfig(config) {
533
+ const errors = [];
534
+ if (config.primaryColor) {
535
+ if (!this.isValidCSSColor(config.primaryColor)) {
536
+ errors.push({
537
+ field: "ui.primaryColor",
538
+ message: "Invalid CSS color format",
539
+ suggestion: "Use valid CSS colors: hex (#FF0000), rgb, hsl, or named colors",
540
+ severity: "warning"
541
+ });
542
+ }
543
+ }
544
+ if (config.borderRadius) {
545
+ const validValues = ["none", "sm", "md", "lg", "xl", "full"];
546
+ if (!validValues.includes(config.borderRadius)) {
547
+ errors.push({
548
+ field: "ui.borderRadius",
549
+ message: `borderRadius must be one of: ${validValues.join(", ")}`,
550
+ severity: "warning"
551
+ });
552
+ }
553
+ }
554
+ if (config.visualStyle) {
555
+ const validValues = ["glass", "solid"];
556
+ if (!validValues.includes(config.visualStyle)) {
557
+ errors.push({
558
+ field: "ui.visualStyle",
559
+ message: `visualStyle must be one of: ${validValues.join(", ")}`,
560
+ severity: "warning"
561
+ });
562
+ }
563
+ }
564
+ return errors;
565
+ }
566
+ static validateRAGConfig(config) {
567
+ const errors = [];
568
+ if (config.topK !== void 0) {
569
+ if (typeof config.topK !== "number" || config.topK <= 0) {
570
+ errors.push({
571
+ field: "rag.topK",
572
+ message: "topK must be a positive integer",
573
+ severity: "error"
574
+ });
575
+ }
576
+ }
577
+ if (config.scoreThreshold !== void 0) {
578
+ if (typeof config.scoreThreshold !== "number" || config.scoreThreshold < 0 || config.scoreThreshold > 1) {
579
+ errors.push({
580
+ field: "rag.scoreThreshold",
581
+ message: "scoreThreshold must be between 0 and 1",
582
+ severity: "error"
583
+ });
584
+ }
585
+ }
586
+ if (config.chunkSize !== void 0) {
587
+ if (typeof config.chunkSize !== "number" || config.chunkSize <= 0) {
588
+ errors.push({
589
+ field: "rag.chunkSize",
590
+ message: "chunkSize must be a positive integer",
591
+ severity: "error"
592
+ });
593
+ }
594
+ }
595
+ if (config.chunkOverlap !== void 0) {
596
+ if (typeof config.chunkOverlap !== "number" || config.chunkOverlap < 0) {
597
+ errors.push({
598
+ field: "rag.chunkOverlap",
599
+ message: "chunkOverlap must be a non-negative integer",
600
+ severity: "error"
601
+ });
602
+ }
603
+ }
604
+ return errors;
605
+ }
606
+ static isValidCSSColor(color) {
607
+ const hexRegex = /^#([0-9a-f]{3}){1,2}$/i;
608
+ const rgbRegex = /^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/i;
609
+ const hslRegex = /^hsla?\((\d+),\s*(\d+)%,\s*(\d+)%(?:,\s*(\d+(?:\.\d+)?))?\)$/i;
610
+ const namedColors = ["red", "blue", "green", "black", "white", "transparent"];
611
+ return hexRegex.test(color) || rgbRegex.test(color) || hslRegex.test(color) || namedColors.includes(color.toLowerCase());
612
+ }
613
+ /**
614
+ * Throws if there are error-level validation issues.
615
+ * Logs warnings to console.
616
+ */
617
+ static validateAndThrow(config) {
618
+ const errors = this.validate(config);
619
+ const errorItems = errors.filter((e) => e.severity === "error");
620
+ const warnings = errors.filter((e) => e.severity === "warning");
621
+ if (warnings.length > 0) {
622
+ console.warn("[ConfigValidator] Configuration warnings:");
623
+ warnings.forEach((w) => {
624
+ console.warn(` ${w.field}: ${w.message}`);
625
+ if (w.suggestion) console.warn(` Suggestion: ${w.suggestion}`);
626
+ });
627
+ }
628
+ if (errorItems.length > 0) {
629
+ const message = errorItems.map((e) => `${e.field}: ${e.message}${e.suggestion ? ` (${e.suggestion})` : ""}`).join("\n ");
630
+ throw new Error(`[ConfigValidator] Configuration validation failed:
631
+ ${message}`);
632
+ }
633
+ }
634
+ };
635
+
636
+ // src/rag/DocumentChunker.ts
637
+ var DocumentChunker = class {
638
+ constructor(chunkSize = 1e3, chunkOverlap = 200) {
639
+ this.chunkSize = chunkSize;
640
+ this.chunkOverlap = chunkOverlap;
641
+ }
642
+ /**
643
+ * Split a single text string into overlapping chunks.
644
+ */
645
+ chunk(text, options = {}) {
646
+ const {
647
+ chunkSize = this.chunkSize,
648
+ chunkOverlap = this.chunkOverlap,
649
+ docId = `doc_${Date.now()}`,
650
+ metadata = {}
651
+ } = options;
652
+ const cleaned = text.replace(/\r\n/g, "\n").trim();
653
+ if (!cleaned) return [];
654
+ const sentences = cleaned.split(new RegExp("(?<=[.!?])\\s+|\\n{2,}")).map((s) => s.trim()).filter(Boolean);
655
+ const chunks = [];
656
+ let current = "";
657
+ let chunkIndex = 0;
658
+ for (const sentence of sentences) {
659
+ if ((current + " " + sentence).trim().length <= chunkSize) {
660
+ current = current ? `${current} ${sentence}` : sentence;
661
+ } else {
662
+ if (current) {
663
+ chunks.push({
664
+ id: `${docId}_chunk_${chunkIndex++}`,
665
+ content: current.trim(),
666
+ metadata: __spreadProps(__spreadValues({}, metadata), { docId, chunkIndex: chunkIndex - 1 })
667
+ });
668
+ }
669
+ if (chunkOverlap > 0 && current.length > chunkOverlap) {
670
+ current = current.slice(-chunkOverlap) + " " + sentence;
671
+ } else {
672
+ current = sentence;
673
+ }
674
+ }
675
+ }
676
+ if (current.trim()) {
677
+ chunks.push({
678
+ id: `${docId}_chunk_${chunkIndex}`,
679
+ content: current.trim(),
680
+ metadata: __spreadProps(__spreadValues({}, metadata), { docId, chunkIndex })
681
+ });
682
+ }
683
+ return chunks;
684
+ }
685
+ /**
686
+ * Chunk multiple documents at once.
687
+ */
688
+ chunkMany(documents) {
689
+ return documents.flatMap(
690
+ (doc) => this.chunk(doc.content, { docId: doc.docId, metadata: doc.metadata })
691
+ );
692
+ }
693
+ };
694
+
695
+ // src/core/ProviderRegistry.ts
696
+ var ProviderRegistry = class {
697
+ /**
698
+ * Register a custom vector provider.
699
+ */
700
+ static registerVectorProvider(name, providerClass) {
701
+ this.vectorProviders[name] = providerClass;
702
+ }
703
+ /**
704
+ * Creates a vector database provider based on the configuration.
705
+ */
706
+ static async createVectorProvider(config) {
707
+ const { provider } = config;
708
+ if (this.vectorProviders[provider]) {
709
+ return new this.vectorProviders[provider](config);
710
+ }
711
+ switch (provider) {
712
+ case "pinecone":
713
+ const { PineconeProvider } = await import("./PineconeProvider-ZRAFNFEC.mjs");
714
+ return new PineconeProvider(config);
715
+ case "pgvector":
716
+ case "postgresql":
717
+ const { PostgreSQLProvider } = await import("./PostgreSQLProvider-ZNXA67IM.mjs");
718
+ return new PostgreSQLProvider(config);
719
+ case "mongodb":
720
+ const { MongoDBProvider } = await import("./MongoDBProvider-WWVJG3WT.mjs");
721
+ return new MongoDBProvider(config);
722
+ case "milvus":
723
+ const { MilvusProvider } = await import("./MilvusProvider-OO6QGZDZ.mjs");
724
+ return new MilvusProvider(config);
725
+ case "qdrant":
726
+ const { QdrantProvider } = await import("./QdrantProvider-VAED5VA7.mjs");
727
+ return new QdrantProvider(config);
728
+ case "chromadb":
729
+ const { ChromaDBProvider } = await import("./ChromaDBProvider-QNI7UCX4.mjs");
730
+ return new ChromaDBProvider(config);
731
+ case "redis":
732
+ const { RedisProvider } = await import("./RedisProvider-ASONNYBI.mjs");
733
+ return new RedisProvider(config);
734
+ case "weaviate":
735
+ const { WeaviateProvider } = await import("./WeaviateProvider-PSDCUGC7.mjs");
736
+ return new WeaviateProvider(config);
737
+ default:
738
+ throw new Error(`[ProviderRegistry] Unsupported vector provider: ${provider}`);
739
+ }
740
+ }
741
+ /**
742
+ * Creates an LLM provider based on the configuration.
743
+ */
744
+ static createLLMProvider(llmConfig, embeddingConfig) {
745
+ return LLMFactory.create(llmConfig, embeddingConfig);
746
+ }
747
+ };
748
+ ProviderRegistry.vectorProviders = {};
749
+
750
+ // src/core/BatchProcessor.ts
751
+ function isTransientError(error) {
752
+ if (!(error instanceof Error)) return false;
753
+ const message = error.message.toLowerCase();
754
+ if (message.includes("econnrefused") || message.includes("econnreset") || message.includes("timeout") || message.includes("network") || message.includes("socket")) {
755
+ return true;
756
+ }
757
+ if (message.includes("429") || message.includes("503") || message.includes("502") || message.includes("rate limit") || message.includes("too many requests")) {
758
+ return true;
759
+ }
760
+ return false;
761
+ }
762
+ function calculateBackoffDelay(attempt, initialDelayMs, maxDelayMs, multiplier) {
763
+ const exponentialDelay = Math.min(
764
+ initialDelayMs * Math.pow(multiplier, attempt),
765
+ maxDelayMs
766
+ );
767
+ const jitter = exponentialDelay * 0.1 * (Math.random() * 2 - 1);
768
+ return Math.max(0, exponentialDelay + jitter);
769
+ }
770
+ function sleep(ms) {
771
+ return new Promise((resolve) => setTimeout(resolve, ms));
772
+ }
773
+ var BatchProcessor = class {
774
+ /**
775
+ * Processes an array of items in configurable batches with retry logic.
776
+ *
777
+ * @param items - Items to process
778
+ * @param processor - Async function that processes a batch of items
779
+ * @param options - Configuration for batch size, retries, etc.
780
+ * @returns Object with results, errors, and statistics
781
+ *
782
+ * @example
783
+ * const docs = [...];
784
+ * const result = await BatchProcessor.processBatch(
785
+ * docs,
786
+ * (batch) => vectorDB.batchUpsert(batch),
787
+ * { batchSize: 100, maxRetries: 3 }
788
+ * );
789
+ */
790
+ static async processBatch(items, processor, options) {
791
+ const {
792
+ batchSize = 100,
793
+ maxRetries = 3,
794
+ initialDelayMs = 100,
795
+ maxDelayMs = 1e4,
796
+ backoffMultiplier = 2,
797
+ throwOnPartialFailure = false
798
+ } = options != null ? options : {};
799
+ const results = [];
800
+ const errors = [];
801
+ const batches = [];
802
+ for (let i = 0; i < items.length; i += batchSize) {
803
+ batches.push(items.slice(i, i + batchSize));
804
+ }
805
+ if (batches.length === 0) {
806
+ return { results: [], errors: [], totalProcessed: 0, totalFailed: 0 };
807
+ }
808
+ for (let batchIndex = 0; batchIndex < batches.length; batchIndex++) {
809
+ const batch = batches[batchIndex];
810
+ let success = false;
811
+ let lastError;
812
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
813
+ try {
814
+ const result = await processor(batch);
815
+ results.push(result);
816
+ success = true;
817
+ break;
818
+ } catch (err) {
819
+ lastError = err instanceof Error ? err : new Error(String(err));
820
+ if (!isTransientError(err) || attempt === maxRetries) {
821
+ break;
822
+ }
823
+ const delay = calculateBackoffDelay(
824
+ attempt,
825
+ initialDelayMs,
826
+ maxDelayMs,
827
+ backoffMultiplier
828
+ );
829
+ await sleep(delay);
830
+ }
831
+ }
832
+ if (!success && lastError) {
833
+ errors.push({
834
+ index: batchIndex,
835
+ error: lastError,
836
+ itemCount: batch.length
837
+ });
838
+ }
839
+ }
840
+ const totalProcessed = items.length - (errors.length > 0 ? errors.reduce((sum, e) => sum + (e.itemCount || 0), 0) : 0);
841
+ const totalFailed = errors.reduce((sum, e) => sum + (e.itemCount || 0), 0);
842
+ if (throwOnPartialFailure && errors.length > 0) {
843
+ const errorMessages = errors.map((e) => `Batch ${e.index} (${e.itemCount} items): ${e.error.message}`).join("\n");
844
+ throw new Error(
845
+ `[BatchProcessor] Batch processing failed:
846
+ ${errorMessages}`
847
+ );
848
+ }
849
+ return { results, errors, totalProcessed, totalFailed };
850
+ }
851
+ /**
852
+ * Processes items sequentially (one at a time) with retry logic.
853
+ * Useful for operations that don't support batching or when granular
854
+ * error tracking is needed.
855
+ */
856
+ static async processSequential(items, processor, options) {
857
+ const {
858
+ maxRetries = 3,
859
+ initialDelayMs = 100,
860
+ maxDelayMs = 1e4,
861
+ backoffMultiplier = 2,
862
+ throwOnPartialFailure = false
863
+ } = options != null ? options : {};
864
+ const results = [];
865
+ const errors = [];
866
+ for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
867
+ const item = items[itemIndex];
868
+ let success = false;
869
+ let lastError;
870
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
871
+ try {
872
+ const result = await processor(item);
873
+ results.push(result);
874
+ success = true;
875
+ break;
876
+ } catch (err) {
877
+ lastError = err instanceof Error ? err : new Error(String(err));
878
+ if (!isTransientError(err) || attempt === maxRetries) {
879
+ break;
880
+ }
881
+ const delay = calculateBackoffDelay(
882
+ attempt,
883
+ initialDelayMs,
884
+ maxDelayMs,
885
+ backoffMultiplier
886
+ );
887
+ await sleep(delay);
888
+ }
889
+ }
890
+ if (!success && lastError) {
891
+ errors.push({ index: itemIndex, error: lastError, itemCount: 1 });
892
+ }
893
+ }
894
+ const totalProcessed = items.length - errors.length;
895
+ const totalFailed = errors.length;
896
+ if (throwOnPartialFailure && errors.length > 0) {
897
+ const errorMessages = errors.map((e) => `Item ${e.index}: ${e.error.message}`).join("\n");
898
+ throw new Error(
899
+ `[BatchProcessor] Sequential processing failed:
900
+ ${errorMessages}`
901
+ );
902
+ }
903
+ return { results, errors, totalProcessed, totalFailed };
904
+ }
905
+ /**
906
+ * Maps over items with retry logic, returning results in order.
907
+ * Like Array.map() but with async processing and automatic retries.
908
+ */
909
+ static async mapWithRetry(items, mapper, options) {
910
+ const result = await this.processSequential(items, mapper, options);
911
+ if (result.errors.length > 0) {
912
+ console.warn(
913
+ `[BatchProcessor] mapWithRetry: ${result.errors.length} items failed`
914
+ );
915
+ }
916
+ const orderedResults = new Array(items.length);
917
+ let resultIndex = 0;
918
+ let errorIndex = 0;
919
+ for (let i = 0; i < items.length; i++) {
920
+ if (errorIndex < result.errors.length && result.errors[errorIndex].index === i) {
921
+ orderedResults[i] = void 0;
922
+ errorIndex++;
923
+ } else {
924
+ orderedResults[i] = result.results[resultIndex];
925
+ resultIndex++;
926
+ }
927
+ }
928
+ return orderedResults.filter((r) => r !== void 0);
929
+ }
930
+ /**
931
+ * Parallel processor with concurrency limit.
932
+ * Processes up to `concurrency` items at the same time.
933
+ */
934
+ static async processConcurrent(items, processor, concurrency = 5, options) {
935
+ const { throwOnPartialFailure = false } = options != null ? options : {};
936
+ const results = [];
937
+ const errors = [];
938
+ for (let i = 0; i < items.length; i += concurrency) {
939
+ const chunk = items.slice(i, i + concurrency).map((item, idx) => ({
940
+ item,
941
+ originalIndex: i + idx
942
+ }));
943
+ const chunkPromises = chunk.map(async ({ item, originalIndex }) => {
944
+ try {
945
+ const result = await processor(item);
946
+ return { success: true, result, index: originalIndex };
947
+ } catch (err) {
948
+ const error = err instanceof Error ? err : new Error(String(err));
949
+ return { success: false, error, index: originalIndex };
950
+ }
951
+ });
952
+ const chunkResults = await Promise.all(chunkPromises);
953
+ for (const { success, result, error, index } of chunkResults) {
954
+ if (success) {
955
+ results.push(result);
956
+ } else {
957
+ errors.push({ index, error, itemCount: 1 });
958
+ }
959
+ }
960
+ }
961
+ const totalProcessed = items.length - errors.length;
962
+ const totalFailed = errors.length;
963
+ if (throwOnPartialFailure && errors.length > 0) {
964
+ const errorMessages = errors.map((e) => `Item ${e.index}: ${e.error.message}`).join("\n");
965
+ throw new Error(
966
+ `[BatchProcessor] Concurrent processing failed:
967
+ ${errorMessages}`
968
+ );
969
+ }
970
+ return { results, errors, totalProcessed, totalFailed };
971
+ }
972
+ };
973
+
974
+ // src/core/Pipeline.ts
975
+ var Pipeline = class {
976
+ constructor(config) {
977
+ this.initialised = false;
978
+ var _a, _b, _c, _d;
979
+ this.config = config;
980
+ this.chunker = new DocumentChunker(
981
+ (_b = (_a = config.rag) == null ? void 0 : _a.chunkSize) != null ? _b : 1e3,
982
+ (_d = (_c = config.rag) == null ? void 0 : _c.chunkOverlap) != null ? _d : 200
983
+ );
984
+ }
985
+ async initialize() {
986
+ if (this.initialised) return;
987
+ this.vectorDB = await ProviderRegistry.createVectorProvider(this.config.vectorDb);
988
+ this.llmProvider = ProviderRegistry.createLLMProvider(this.config.llm, this.config.embedding);
989
+ if (this.config.llm.provider === "anthropic") {
990
+ const { LLMFactory: LLMFactory2 } = await import("./LLMFactory-JFOY2V4X.mjs");
991
+ this.embeddingProvider = LLMFactory2.createEmbeddingProvider(this.config.embedding);
992
+ } else {
993
+ this.embeddingProvider = this.llmProvider;
994
+ }
995
+ await this.vectorDB.initialize();
996
+ this.initialised = true;
997
+ }
998
+ /**
999
+ * Ingest documents with automatic chunking, embedding, and batch processing.
1000
+ * Handles retries for transient failures.
1001
+ */
1002
+ async ingest(documents, namespace) {
1003
+ await this.initialize();
1004
+ const ns = namespace != null ? namespace : this.config.projectId;
1005
+ const results = [];
1006
+ for (const doc of documents) {
1007
+ try {
1008
+ const chunks = this.chunker.chunk(doc.content, {
1009
+ docId: doc.docId,
1010
+ metadata: doc.metadata
1011
+ });
1012
+ const batchOptions = {
1013
+ batchSize: 50,
1014
+ // Embedding batch size
1015
+ maxRetries: 3,
1016
+ initialDelayMs: 100
1017
+ };
1018
+ const vectors = await BatchProcessor.mapWithRetry(
1019
+ chunks.map((c) => c.content),
1020
+ (text) => this.embeddingProvider.embed(text),
1021
+ batchOptions
1022
+ );
1023
+ if (vectors.length !== chunks.length) {
1024
+ throw new Error(`Embedding failed: got ${vectors.length} vectors for ${chunks.length} chunks`);
1025
+ }
1026
+ const upsertDocs = chunks.map((chunk, i) => ({
1027
+ id: chunk.id,
1028
+ vector: vectors[i],
1029
+ content: chunk.content,
1030
+ metadata: chunk.metadata
1031
+ }));
1032
+ const upsertBatchOptions = {
1033
+ batchSize: 100,
1034
+ maxRetries: 3,
1035
+ initialDelayMs: 100
1036
+ };
1037
+ const upsertResult = await BatchProcessor.processBatch(
1038
+ upsertDocs,
1039
+ (batch) => this.vectorDB.batchUpsert(batch, ns),
1040
+ upsertBatchOptions
1041
+ );
1042
+ if (upsertResult.errors.length > 0) {
1043
+ console.warn(`[Pipeline] Ingestion: ${upsertResult.errors.length} batches failed for doc ${doc.docId}`);
1044
+ }
1045
+ results.push({
1046
+ docId: doc.docId,
1047
+ chunksIngested: upsertResult.totalProcessed
1048
+ });
1049
+ } catch (error) {
1050
+ console.error(`[Pipeline] Failed to ingest document ${doc.docId}:`, error);
1051
+ results.push({
1052
+ docId: doc.docId,
1053
+ chunksIngested: 0
1054
+ });
1055
+ }
1056
+ }
1057
+ return results;
1058
+ }
1059
+ async ask(question, history = [], namespace) {
1060
+ var _a, _b, _c, _d;
1061
+ await this.initialize();
1062
+ const ns = namespace != null ? namespace : this.config.projectId;
1063
+ const topK = (_b = (_a = this.config.rag) == null ? void 0 : _a.topK) != null ? _b : 5;
1064
+ const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0;
1065
+ try {
1066
+ const queryVector = await this.embeddingProvider.embed(question);
1067
+ const rawMatches = await this.vectorDB.query(queryVector, topK, ns);
1068
+ const sources = rawMatches.filter((m) => m.score >= scoreThreshold);
1069
+ const context = sources.length ? sources.map((m, i) => `[Source ${i + 1}]
1070
+ ${m.content}`).join("\n\n---\n\n") : "No relevant context found.";
1071
+ const messages = [...history, { role: "user", content: question }];
1072
+ const reply = await this.llmProvider.chat(messages, context);
1073
+ return { reply, sources };
1074
+ } catch (error) {
1075
+ throw new Error(`[Pipeline] Chat failed: ${error instanceof Error ? error.message : String(error)}`);
1076
+ }
1077
+ }
1078
+ };
1079
+
1080
+ // src/core/ProviderHealthCheck.ts
1081
+ var ProviderHealthCheck = class {
1082
+ /**
1083
+ * Validates vector database configuration before initialization.
1084
+ * Performs connectivity checks and verifies required resources exist.
1085
+ */
1086
+ static async checkVectorProvider(config) {
1087
+ const timestamp = Date.now();
1088
+ const configErrors = ConfigValidator.validate({
1089
+ projectId: "health-check",
1090
+ vectorDb: config,
1091
+ llm: { provider: "openai", model: "gpt-4o" },
1092
+ // dummy
1093
+ embedding: { provider: "openai", model: "text-embedding-3-small" }
1094
+ });
1095
+ const vectorDbErrors = configErrors.filter((e) => e.field.startsWith("vectorDb"));
1096
+ if (vectorDbErrors.length > 0) {
1097
+ return {
1098
+ healthy: false,
1099
+ provider: config.provider,
1100
+ error: `Configuration validation failed: ${vectorDbErrors.map((e) => e.message).join("; ")}`,
1101
+ timestamp
1102
+ };
1103
+ }
1104
+ try {
1105
+ switch (config.provider) {
1106
+ case "pinecone":
1107
+ return await this.checkPinecone(config, timestamp);
1108
+ case "pgvector":
1109
+ case "postgresql":
1110
+ return await this.checkPostgres(config, timestamp);
1111
+ case "mongodb":
1112
+ return await this.checkMongoDB(config, timestamp);
1113
+ case "milvus":
1114
+ return await this.checkMilvus(config, timestamp);
1115
+ case "qdrant":
1116
+ return await this.checkQdrant(config, timestamp);
1117
+ case "chromadb":
1118
+ return await this.checkChromaDB(config, timestamp);
1119
+ case "redis":
1120
+ return await this.checkRedis(config, timestamp);
1121
+ case "weaviate":
1122
+ return await this.checkWeaviate(config, timestamp);
1123
+ case "rest":
1124
+ case "universal_rest":
1125
+ return await this.checkRestAPI(config, timestamp);
1126
+ default:
1127
+ return {
1128
+ healthy: false,
1129
+ provider: config.provider,
1130
+ error: `Unsupported provider: ${config.provider}`,
1131
+ timestamp
1132
+ };
1133
+ }
1134
+ } catch (error) {
1135
+ return {
1136
+ healthy: false,
1137
+ provider: config.provider,
1138
+ error: error instanceof Error ? error.message : String(error),
1139
+ timestamp
1140
+ };
1141
+ }
1142
+ }
1143
+ static async checkPinecone(config, timestamp) {
1144
+ var _a, _b;
1145
+ const opts = config.options;
1146
+ try {
1147
+ const { Pinecone } = await import("@pinecone-database/pinecone");
1148
+ const client = new Pinecone({ apiKey: opts.apiKey });
1149
+ const indexes = await client.listIndexes();
1150
+ const indexNames = (_b = (_a = indexes.indexes) == null ? void 0 : _a.map((i) => i.name)) != null ? _b : [];
1151
+ if (!indexNames.includes(config.indexName)) {
1152
+ return {
1153
+ healthy: false,
1154
+ provider: "pinecone",
1155
+ error: `Index "${config.indexName}" not found. Available: ${indexNames.join(", ")}`,
1156
+ timestamp
1157
+ };
1158
+ }
1159
+ return {
1160
+ healthy: true,
1161
+ provider: "pinecone",
1162
+ capabilities: {
1163
+ indexes: indexNames.length,
1164
+ targetIndex: config.indexName
1165
+ },
1166
+ timestamp
1167
+ };
1168
+ } catch (error) {
1169
+ return {
1170
+ healthy: false,
1171
+ provider: "pinecone",
1172
+ error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
1173
+ timestamp
1174
+ };
1175
+ }
1176
+ }
1177
+ static async checkPostgres(config, timestamp) {
1178
+ const opts = config.options;
1179
+ try {
1180
+ const { Client } = await import("pg");
1181
+ const client = new Client({ connectionString: opts.connectionString });
1182
+ await client.connect();
1183
+ const result = await client.query(`
1184
+ SELECT EXISTS(
1185
+ SELECT 1 FROM pg_extension WHERE extname = 'vector'
1186
+ );
1187
+ `);
1188
+ const hasVector = result.rows[0].exists;
1189
+ await client.end();
1190
+ return {
1191
+ healthy: true,
1192
+ provider: "postgresql",
1193
+ capabilities: { pgvectorInstalled: hasVector },
1194
+ timestamp
1195
+ };
1196
+ } catch (error) {
1197
+ return {
1198
+ healthy: false,
1199
+ provider: "postgresql",
1200
+ error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
1201
+ timestamp
1202
+ };
1203
+ }
1204
+ }
1205
+ static async checkMongoDB(config, timestamp) {
1206
+ const opts = config.options;
1207
+ try {
1208
+ const { MongoClient } = await import("mongodb");
1209
+ const client = new MongoClient(opts.uri);
1210
+ await client.connect();
1211
+ const db = client.db(opts.database);
1212
+ const collections = await db.listCollections().toArray();
1213
+ const collectionNames = collections.map((c) => c.name);
1214
+ const hasCollection = collectionNames.includes(opts.collection);
1215
+ await client.close();
1216
+ return {
1217
+ healthy: true,
1218
+ provider: "mongodb",
1219
+ capabilities: {
1220
+ collections: collectionNames.length,
1221
+ targetCollection: hasCollection ? opts.collection : "NOT FOUND"
1222
+ },
1223
+ timestamp
1224
+ };
1225
+ } catch (error) {
1226
+ return {
1227
+ healthy: false,
1228
+ provider: "mongodb",
1229
+ error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
1230
+ timestamp
1231
+ };
1232
+ }
1233
+ }
1234
+ static async checkMilvus(config, timestamp) {
1235
+ const opts = config.options;
1236
+ const host = opts.host || "localhost";
1237
+ const port = opts.port || 19530;
1238
+ try {
1239
+ const controller = new AbortController();
1240
+ const timeoutId = setTimeout(() => controller.abort(), 5e3);
1241
+ const response = await fetch(`http://${host}:${port}/healthz`, {
1242
+ signal: controller.signal
1243
+ });
1244
+ clearTimeout(timeoutId);
1245
+ if (!response.ok) {
1246
+ throw new Error(`Health check returned ${response.status}`);
1247
+ }
1248
+ return {
1249
+ healthy: true,
1250
+ provider: "milvus",
1251
+ capabilities: { endpoint: `${host}:${port}` },
1252
+ timestamp
1253
+ };
1254
+ } catch (error) {
1255
+ return {
1256
+ healthy: false,
1257
+ provider: "milvus",
1258
+ error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
1259
+ timestamp
1260
+ };
1261
+ }
1262
+ }
1263
+ static async checkQdrant(config, timestamp) {
1264
+ const opts = config.options;
1265
+ const baseUrl = opts.baseUrl || opts.url || "http://localhost:6333";
1266
+ try {
1267
+ const apiKey = opts.apiKey;
1268
+ const response = await fetch(`${baseUrl}/`, {
1269
+ headers: apiKey ? { "api-key": apiKey } : {}
1270
+ });
1271
+ if (!response.ok) {
1272
+ throw new Error(`Health check returned ${response.status}`);
1273
+ }
1274
+ const health = await response.json();
1275
+ return {
1276
+ healthy: true,
1277
+ provider: "qdrant",
1278
+ capabilities: health,
1279
+ timestamp
1280
+ };
1281
+ } catch (error) {
1282
+ return {
1283
+ healthy: false,
1284
+ provider: "qdrant",
1285
+ error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
1286
+ timestamp
1287
+ };
1288
+ }
1289
+ }
1290
+ static async checkChromaDB(config, timestamp) {
1291
+ const opts = config.options;
1292
+ const host = opts.host || "localhost";
1293
+ const port = opts.port || 8e3;
1294
+ try {
1295
+ const response = await fetch(`http://${host}:${port}/api/v1/heartbeat`);
1296
+ if (!response.ok) {
1297
+ throw new Error(`Health check returned ${response.status}`);
1298
+ }
1299
+ return {
1300
+ healthy: true,
1301
+ provider: "chromadb",
1302
+ capabilities: { endpoint: `${host}:${port}` },
1303
+ timestamp
1304
+ };
1305
+ } catch (error) {
1306
+ return {
1307
+ healthy: false,
1308
+ provider: "chromadb",
1309
+ error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
1310
+ timestamp
1311
+ };
1312
+ }
1313
+ }
1314
+ static async checkRedis(config, timestamp) {
1315
+ const opts = config.options;
1316
+ const url = opts.url || `redis://${opts.host}:${opts.port}`;
1317
+ try {
1318
+ await fetch(url);
1319
+ return {
1320
+ healthy: true,
1321
+ provider: "redis",
1322
+ capabilities: { endpoint: url },
1323
+ timestamp
1324
+ };
1325
+ } catch (error) {
1326
+ return {
1327
+ healthy: false,
1328
+ provider: "redis",
1329
+ error: `Connection check failed: ${error instanceof Error ? error.message : String(error)}. Ensure Redis is running at ${url}`,
1330
+ timestamp
1331
+ };
1332
+ }
1333
+ }
1334
+ static async checkWeaviate(config, timestamp) {
1335
+ const opts = config.options;
1336
+ const url = (opts.url || "http://localhost:8080").replace(/\/$/, "");
1337
+ try {
1338
+ const response = await fetch(`${url}/.well-known/ready`);
1339
+ if (!response.ok) {
1340
+ throw new Error(`Health check returned ${response.status}`);
1341
+ }
1342
+ return {
1343
+ healthy: true,
1344
+ provider: "weaviate",
1345
+ capabilities: { endpoint: url },
1346
+ timestamp
1347
+ };
1348
+ } catch (error) {
1349
+ return {
1350
+ healthy: false,
1351
+ provider: "weaviate",
1352
+ error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
1353
+ timestamp
1354
+ };
1355
+ }
1356
+ }
1357
+ static async checkRestAPI(config, timestamp) {
1358
+ const opts = config.options;
1359
+ const baseUrl = (opts.baseUrl || "").replace(/\/$/, "");
1360
+ if (!baseUrl) {
1361
+ return {
1362
+ healthy: false,
1363
+ provider: "rest",
1364
+ error: "baseUrl is required",
1365
+ timestamp
1366
+ };
1367
+ }
1368
+ try {
1369
+ const response = await fetch(`${baseUrl}/health`, {
1370
+ headers: opts.headers ? JSON.parse(opts.headers) : {}
1371
+ });
1372
+ return {
1373
+ healthy: response.ok,
1374
+ provider: "rest",
1375
+ error: response.ok ? void 0 : `Health check returned ${response.status}`,
1376
+ timestamp
1377
+ };
1378
+ } catch (error) {
1379
+ return {
1380
+ healthy: false,
1381
+ provider: "rest",
1382
+ error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
1383
+ timestamp
1384
+ };
1385
+ }
1386
+ }
1387
+ /**
1388
+ * Validates LLM provider configuration.
1389
+ */
1390
+ static async checkLLMProvider(config) {
1391
+ const timestamp = Date.now();
1392
+ try {
1393
+ switch (config.provider) {
1394
+ case "openai":
1395
+ return await this.checkOpenAI(config, timestamp);
1396
+ case "anthropic":
1397
+ return await this.checkAnthropic(config, timestamp);
1398
+ case "ollama":
1399
+ return await this.checkOllama(config, timestamp);
1400
+ case "rest":
1401
+ case "universal_rest":
1402
+ return await this.checkLLMRestAPI(config, timestamp);
1403
+ default:
1404
+ return {
1405
+ healthy: false,
1406
+ provider: config.provider,
1407
+ error: `Unsupported provider: ${config.provider}`,
1408
+ timestamp
1409
+ };
1410
+ }
1411
+ } catch (error) {
1412
+ return {
1413
+ healthy: false,
1414
+ provider: config.provider,
1415
+ error: error instanceof Error ? error.message : String(error),
1416
+ timestamp
1417
+ };
1418
+ }
1419
+ }
1420
+ static async checkOpenAI(config, timestamp) {
1421
+ try {
1422
+ const OpenAI = await import("openai");
1423
+ const client = new OpenAI.default({ apiKey: config.apiKey });
1424
+ const models = await client.models.list();
1425
+ const hasModel = models.data.some((m) => m.id === config.model);
1426
+ return {
1427
+ healthy: true,
1428
+ provider: "openai",
1429
+ capabilities: {
1430
+ model: config.model,
1431
+ available: hasModel,
1432
+ totalModels: models.data.length
1433
+ },
1434
+ timestamp
1435
+ };
1436
+ } catch (error) {
1437
+ return {
1438
+ healthy: false,
1439
+ provider: "openai",
1440
+ error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
1441
+ timestamp
1442
+ };
1443
+ }
1444
+ }
1445
+ static async checkAnthropic(config, timestamp) {
1446
+ try {
1447
+ const { default: Anthropic } = await import("@anthropic-ai/sdk");
1448
+ const client = new Anthropic({ apiKey: config.apiKey });
1449
+ await client.messages.create({
1450
+ model: config.model,
1451
+ max_tokens: 10,
1452
+ messages: [{ role: "user", content: "ping" }]
1453
+ });
1454
+ return {
1455
+ healthy: true,
1456
+ provider: "anthropic",
1457
+ capabilities: { model: config.model },
1458
+ timestamp
1459
+ };
1460
+ } catch (error) {
1461
+ return {
1462
+ healthy: false,
1463
+ provider: "anthropic",
1464
+ error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
1465
+ timestamp
1466
+ };
1467
+ }
1468
+ }
1469
+ static async checkOllama(config, timestamp) {
1470
+ const baseUrl = (config.baseUrl || "http://localhost:11434").replace(/\/$/, "");
1471
+ try {
1472
+ const response = await fetch(`${baseUrl}/api/tags`);
1473
+ if (!response.ok) {
1474
+ throw new Error(`Health check returned ${response.status}`);
1475
+ }
1476
+ const data = await response.json();
1477
+ const models = data.models || [];
1478
+ const hasModel = models.some((m) => m.name === config.model);
1479
+ return {
1480
+ healthy: true,
1481
+ provider: "ollama",
1482
+ capabilities: {
1483
+ model: config.model,
1484
+ available: hasModel,
1485
+ totalModels: models.length
1486
+ },
1487
+ timestamp
1488
+ };
1489
+ } catch (error) {
1490
+ return {
1491
+ healthy: false,
1492
+ provider: "ollama",
1493
+ error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
1494
+ timestamp
1495
+ };
1496
+ }
1497
+ }
1498
+ static async checkLLMRestAPI(config, timestamp) {
1499
+ var _a, _b;
1500
+ const baseUrlRaw = config.baseUrl || ((_a = config.options) == null ? void 0 : _a.baseUrl);
1501
+ const baseUrl = (typeof baseUrlRaw === "string" ? baseUrlRaw : "").replace(/\/$/, "");
1502
+ if (!baseUrl) {
1503
+ return {
1504
+ healthy: false,
1505
+ provider: config.provider,
1506
+ error: "baseUrl is required",
1507
+ timestamp
1508
+ };
1509
+ }
1510
+ try {
1511
+ const headers = (_b = config.options) == null ? void 0 : _b.headers;
1512
+ const response = await fetch(`${baseUrl}/health`, {
1513
+ headers: headers || void 0
1514
+ });
1515
+ return {
1516
+ healthy: response.ok,
1517
+ provider: config.provider,
1518
+ error: response.ok ? void 0 : `Health check returned ${response.status}`,
1519
+ timestamp
1520
+ };
1521
+ } catch (error) {
1522
+ return {
1523
+ healthy: false,
1524
+ provider: config.provider,
1525
+ error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
1526
+ timestamp
1527
+ };
1528
+ }
1529
+ }
1530
+ /**
1531
+ * Runs comprehensive health checks on all configured providers.
1532
+ */
1533
+ static async checkAll(vectorDbConfig, llmConfig, embeddingConfig) {
1534
+ const [vectorDb, llm, embedding] = await Promise.all([
1535
+ this.checkVectorProvider(vectorDbConfig),
1536
+ this.checkLLMProvider(llmConfig),
1537
+ embeddingConfig ? this.checkLLMProvider(embeddingConfig) : Promise.resolve(void 0)
1538
+ ]);
1539
+ return {
1540
+ vectorDb,
1541
+ llm,
1542
+ embedding,
1543
+ allHealthy: vectorDb.healthy && llm.healthy && (!embedding || embedding.healthy)
1544
+ };
1545
+ }
1546
+ };
1547
+
1548
+ // src/core/VectorPlugin.ts
1549
+ var VectorPlugin = class {
1550
+ /**
1551
+ * Initializes the plugin with the host configuration.
1552
+ * @param hostConfig - Configuration object passed from the host application.
1553
+ * @throws Error if configuration is invalid or providers are unhealthy
1554
+ *
1555
+ * @example
1556
+ * const plugin = new VectorPlugin({
1557
+ * projectId: 'my-app',
1558
+ * vectorDb: {
1559
+ * provider: 'pinecone',
1560
+ * indexName: 'my-index',
1561
+ * options: { apiKey: process.env.PINECONE_API_KEY }
1562
+ * },
1563
+ * llm: {
1564
+ * provider: 'openai',
1565
+ * model: 'gpt-4o',
1566
+ * apiKey: process.env.OPENAI_API_KEY
1567
+ * }
1568
+ * });
1569
+ */
1570
+ constructor(hostConfig) {
1571
+ this.config = ConfigResolver.resolve(hostConfig);
1572
+ ConfigValidator.validateAndThrow(this.config);
1573
+ this.pipeline = new Pipeline(this.config);
1574
+ }
1575
+ /**
1576
+ * Get the current resolved configuration.
1577
+ */
1578
+ getConfig() {
1579
+ return this.config;
1580
+ }
1581
+ /**
1582
+ * Perform pre-flight health checks on all configured providers.
1583
+ * Useful to verify connectivity before running operations.
1584
+ *
1585
+ * @returns Health status for vector DB, LLM, and embedding providers
1586
+ */
1587
+ async checkHealth() {
1588
+ return ProviderHealthCheck.checkAll(
1589
+ this.config.vectorDb,
1590
+ this.config.llm,
1591
+ this.config.embedding
1592
+ );
1593
+ }
1594
+ /**
1595
+ * Run a chat query.
1596
+ */
1597
+ async chat(message, history = [], namespace) {
1598
+ return this.pipeline.ask(message, history, namespace);
1599
+ }
1600
+ /**
1601
+ * Ingest documents into the vector database.
1602
+ */
1603
+ async ingest(documents, namespace) {
1604
+ return this.pipeline.ingest(documents, namespace);
1605
+ }
1606
+ };
1607
+
1608
+ // src/utils/DocumentParser.ts
1609
+ var DocumentParser = class {
1610
+ /**
1611
+ * Extract text from a File or Buffer based on its type.
1612
+ */
1613
+ static async parse(file, fileName, mimeType) {
1614
+ var _a;
1615
+ const extension = (_a = fileName.split(".").pop()) == null ? void 0 : _a.toLowerCase();
1616
+ if (extension === "txt" || extension === "md" || mimeType === "text/plain" || mimeType === "text/markdown") {
1617
+ return this.readAsText(file);
1618
+ }
1619
+ if (extension === "json" || mimeType === "application/json") {
1620
+ const text = await this.readAsText(file);
1621
+ try {
1622
+ const obj = JSON.parse(text);
1623
+ return JSON.stringify(obj, null, 2);
1624
+ } catch (e) {
1625
+ return text;
1626
+ }
1627
+ }
1628
+ if (extension === "csv" || mimeType === "text/csv") {
1629
+ return this.readAsText(file);
1630
+ }
1631
+ if (extension === "pdf" || mimeType === "application/pdf") {
1632
+ try {
1633
+ const pdf = await import("pdf-parse/lib/pdf-parse.js");
1634
+ const buffer = Buffer.isBuffer(file) ? file : Buffer.from(await file.arrayBuffer());
1635
+ const data = await pdf.default(buffer);
1636
+ return data.text;
1637
+ } catch (err) {
1638
+ console.warn('[DocumentParser] PDF parsing failed. Make sure "pdf-parse" is installed.', err);
1639
+ return `[PDF Parsing Error for ${fileName}]`;
1640
+ }
1641
+ }
1642
+ if (extension === "docx" || mimeType === "application/vnd.openxmlformats-officedocument.wordprocessingml.document") {
1643
+ try {
1644
+ const mammoth = await import("mammoth");
1645
+ const buffer = Buffer.isBuffer(file) ? file : Buffer.from(await file.arrayBuffer());
1646
+ const result = await mammoth.extractRawText({ buffer });
1647
+ return result.value;
1648
+ } catch (err) {
1649
+ console.warn('[DocumentParser] DOCX parsing failed. Make sure "mammoth" is installed.', err);
1650
+ return `[DOCX Parsing Error for ${fileName}]`;
1651
+ }
1652
+ }
1653
+ try {
1654
+ return await this.readAsText(file);
1655
+ } catch (e) {
1656
+ throw new Error(`[DocumentParser] Unsupported file format: ${fileName} (${mimeType})`);
1657
+ }
1658
+ }
1659
+ static async readAsText(file) {
1660
+ if (Buffer.isBuffer(file)) {
1661
+ return file.toString("utf-8");
1662
+ }
1663
+ return await file.text();
1664
+ }
1665
+ };
1666
+
1667
+ // src/handlers/index.ts
1668
+ function createChatHandler(config) {
1669
+ const plugin = new VectorPlugin(config);
1670
+ return async function POST(req) {
1671
+ try {
1672
+ const body = await req.json();
1673
+ const { message, history = [], namespace } = body;
1674
+ if (!(message == null ? void 0 : message.trim())) {
1675
+ return NextResponse.json({ error: "message is required" }, { status: 400 });
1676
+ }
1677
+ const result = await plugin.chat(message, history, namespace);
1678
+ return NextResponse.json(result);
1679
+ } catch (err) {
1680
+ const message = err instanceof Error ? err.message : "Internal server error";
1681
+ return NextResponse.json({ error: message }, { status: 500 });
1682
+ }
1683
+ };
1684
+ }
1685
+ function createIngestHandler(config) {
1686
+ const plugin = new VectorPlugin(config);
1687
+ return async function POST(req) {
1688
+ try {
1689
+ const body = await req.json();
1690
+ const { documents, namespace } = body;
1691
+ if (!Array.isArray(documents) || documents.length === 0) {
1692
+ return NextResponse.json({ error: "documents array is required" }, { status: 400 });
1693
+ }
1694
+ const results = await plugin.ingest(documents, namespace);
1695
+ return NextResponse.json({ results });
1696
+ } catch (err) {
1697
+ const message = err instanceof Error ? err.message : "Internal server error";
1698
+ return NextResponse.json({ error: message }, { status: 500 });
1699
+ }
1700
+ };
1701
+ }
1702
+ function createHealthHandler(config) {
1703
+ const plugin = new VectorPlugin(config);
1704
+ return async function GET() {
1705
+ try {
1706
+ const health = await plugin.checkHealth();
1707
+ const status = health.allHealthy ? "ok" : "degraded";
1708
+ return NextResponse.json(__spreadProps(__spreadValues({
1709
+ status
1710
+ }, health), {
1711
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1712
+ }));
1713
+ } catch (err) {
1714
+ const message = err instanceof Error ? err.message : "Unknown error";
1715
+ return NextResponse.json({ status: "error", error: message }, { status: 500 });
1716
+ }
1717
+ };
1718
+ }
1719
+ function createUploadHandler(config) {
1720
+ const plugin = new VectorPlugin(config);
1721
+ return async function POST(req) {
1722
+ try {
1723
+ const formData = await req.formData();
1724
+ const files = formData.getAll("files");
1725
+ const namespace = formData.get("namespace") || void 0;
1726
+ if (!files || files.length === 0) {
1727
+ return NextResponse.json({ error: "No files provided" }, { status: 400 });
1728
+ }
1729
+ const documents = await Promise.all(
1730
+ files.map(async (file) => {
1731
+ const content = await DocumentParser.parse(file, file.name, file.type);
1732
+ return {
1733
+ docId: file.name,
1734
+ content,
1735
+ metadata: {
1736
+ fileName: file.name,
1737
+ fileSize: file.size,
1738
+ fileType: file.type,
1739
+ uploadedAt: (/* @__PURE__ */ new Date()).toISOString()
1740
+ }
1741
+ };
1742
+ })
1743
+ );
1744
+ const results = await plugin.ingest(documents, namespace);
1745
+ return NextResponse.json({ message: "Upload successful", results });
1746
+ } catch (err) {
1747
+ const message = err instanceof Error ? err.message : "Upload failed";
1748
+ return NextResponse.json({ error: message }, { status: 500 });
1749
+ }
1750
+ };
1751
+ }
1752
+
1753
+ export {
1754
+ getRagConfig,
1755
+ ConfigResolver,
1756
+ DocumentChunker,
1757
+ ProviderRegistry,
1758
+ Pipeline,
1759
+ VectorPlugin,
1760
+ createChatHandler,
1761
+ createIngestHandler,
1762
+ createHealthHandler,
1763
+ createUploadHandler
1764
+ };