@retrivora-ai/rag-engine 0.4.4 → 1.0.0

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 (73) hide show
  1. package/README.md +32 -57
  2. package/dist/{ChromaDBProvider-GI7TB7GJ.mjs → ChromaDBProvider-APQVJ5F7.mjs} +2 -2
  3. package/dist/{DocumentChunker-3yElxTO3.d.mts → DocumentChunker-C-sCZPhi.d.mts} +6 -6
  4. package/dist/{DocumentChunker-3yElxTO3.d.ts → DocumentChunker-C-sCZPhi.d.ts} +6 -6
  5. package/dist/{MilvusProvider-WDVTFB7D.mjs → MilvusProvider-35US67MS.mjs} +2 -2
  6. package/dist/{MongoDBProvider-ZKW34AEL.mjs → MongoDBProvider-COVYZDP6.mjs} +2 -2
  7. package/dist/{PineconeProvider-BE2JWSPD.mjs → PineconeProvider-AWFJQDZL.mjs} +2 -2
  8. package/dist/{PostgreSQLProvider-5HHTK4SU.mjs → PostgreSQLProvider-IEYRJ7XJ.mjs} +2 -2
  9. package/dist/{QdrantProvider-XVDVBNIG.mjs → QdrantProvider-M6TQYZRO.mjs} +2 -2
  10. package/dist/{RagConfig-BgRDL9Vy.d.mts → RagConfig-DRJO4hGU.d.mts} +12 -1
  11. package/dist/{RagConfig-BgRDL9Vy.d.ts → RagConfig-DRJO4hGU.d.ts} +12 -1
  12. package/dist/{RedisProvider-EK2R2PQH.mjs → RedisProvider-3G5PBLZ4.mjs} +2 -2
  13. package/dist/{SimpleGraphProvider-M6T7SE7D.mjs → SimpleGraphProvider-UK7DJW37.mjs} +1 -1
  14. package/dist/{UniversalVectorProvider-YIDRX6VT.mjs → UniversalVectorProvider-FYQ3B2PW.mjs} +3 -3
  15. package/dist/{WeaviateProvider-4CAPQ7UY.mjs → WeaviateProvider-ITHO36IL.mjs} +2 -2
  16. package/dist/{chunk-5KNBWQM6.mjs → chunk-4A47RCG2.mjs} +5 -1
  17. package/dist/{chunk-EDLTMSNY.mjs → chunk-67AJ6SMD.mjs} +1 -1
  18. package/dist/{chunk-IWHCAQEA.mjs → chunk-7SOSCZGS.mjs} +68 -7
  19. package/dist/{chunk-LJWWPTWE.mjs → chunk-FLOSGE6A.mjs} +76 -14
  20. package/dist/{chunk-H6RKMU7W.mjs → chunk-NXUCKY5L.mjs} +1 -1
  21. package/dist/{chunk-KTS3LLHY.mjs → chunk-OOQXNLXD.mjs} +5 -5
  22. package/dist/{chunk-OKY5P6RA.mjs → chunk-P4HAQ7KB.mjs} +1186 -1345
  23. package/dist/chunk-QMIKLALV.mjs +57 -0
  24. package/dist/{chunk-3QWAK3RZ.mjs → chunk-TYHTZIDP.mjs} +6 -2
  25. package/dist/{chunk-GQT5LF4G.mjs → chunk-U6KHVZLF.mjs} +2 -2
  26. package/dist/{chunk-RK2UDJA2.mjs → chunk-WGSZNY3X.mjs} +1 -1
  27. package/dist/{chunk-XCNXPECE.mjs → chunk-ZNBKHNJ4.mjs} +55 -1
  28. package/dist/handlers/index.d.mts +2 -2
  29. package/dist/handlers/index.d.ts +2 -2
  30. package/dist/handlers/index.js +1417 -1377
  31. package/dist/handlers/index.mjs +3 -3
  32. package/dist/index-CrGMwXfO.d.ts +112 -0
  33. package/dist/index-v669iV-k.d.mts +112 -0
  34. package/dist/index.d.mts +5 -5
  35. package/dist/index.d.ts +5 -5
  36. package/dist/index.mjs +2 -2
  37. package/dist/server.d.mts +104 -158
  38. package/dist/server.d.ts +104 -158
  39. package/dist/server.js +1419 -1379
  40. package/dist/server.mjs +12 -12
  41. package/package.json +5 -1
  42. package/src/components/DocumentUpload.tsx +1 -1
  43. package/src/config/RagConfig.ts +7 -0
  44. package/src/core/ConfigValidator.ts +66 -492
  45. package/src/core/LangChainAgent.ts +78 -0
  46. package/src/core/Pipeline.ts +210 -222
  47. package/src/core/ProviderHealthCheck.ts +35 -406
  48. package/src/core/ProviderInterfaces.ts +37 -0
  49. package/src/core/ProviderRegistry.ts +70 -55
  50. package/src/core/QueryProcessor.ts +173 -0
  51. package/src/llm/ILLMProvider.ts +10 -0
  52. package/src/llm/LLMFactory.ts +33 -13
  53. package/src/llm/providers/AnthropicProvider.ts +55 -15
  54. package/src/llm/providers/GeminiProvider.ts +51 -0
  55. package/src/llm/providers/OllamaProvider.ts +100 -15
  56. package/src/llm/providers/OpenAIProvider.ts +60 -11
  57. package/src/providers/vectordb/BaseVectorProvider.ts +11 -0
  58. package/src/providers/vectordb/MilvusProvider.ts +4 -0
  59. package/src/providers/vectordb/MongoDBProvider.ts +75 -11
  60. package/src/providers/vectordb/PineconeProvider.ts +60 -5
  61. package/src/providers/vectordb/PostgreSQLProvider.ts +84 -14
  62. package/src/providers/vectordb/QdrantProvider.ts +4 -0
  63. package/src/providers/vectordb/WeaviateProvider.ts +8 -4
  64. package/src/rag/DocumentChunker.ts +15 -19
  65. package/src/rag/EntityExtractor.ts +1 -1
  66. package/src/rag/LlamaIndexIngestor.ts +61 -0
  67. package/src/rag/Reranker.ts +20 -0
  68. package/src/server.ts +1 -1
  69. package/src/types/index.ts +9 -0
  70. package/src/utils/DocumentParser.ts +1 -1
  71. package/dist/chunk-FWCSY2DS.mjs +0 -37
  72. package/dist/index-7qeLTPBL.d.mts +0 -114
  73. package/dist/index-DowY4_K0.d.ts +0 -114
@@ -2,11 +2,14 @@ import {
2
2
  buildPayload,
3
3
  mergeDefined,
4
4
  resolvePath
5
- } from "./chunk-EDLTMSNY.mjs";
5
+ } from "./chunk-67AJ6SMD.mjs";
6
6
  import {
7
+ __asyncGenerator,
8
+ __await,
9
+ __forAwait,
7
10
  __spreadProps,
8
11
  __spreadValues
9
- } from "./chunk-FWCSY2DS.mjs";
12
+ } from "./chunk-QMIKLALV.mjs";
10
13
 
11
14
  // src/handlers/index.ts
12
15
  import { NextResponse } from "next/server";
@@ -50,7 +53,6 @@ var PROVIDERS_WITH_EMBEDDINGS = [
50
53
  "rest",
51
54
  "universal_rest"
52
55
  ];
53
- var UI_VISUAL_STYLES = ["glass", "solid"];
54
56
  var UI_BORDER_RADIUS_OPTIONS = ["none", "sm", "md", "lg", "xl", "full"];
55
57
 
56
58
  // src/config/serverConfig.ts
@@ -228,657 +230,68 @@ var ConfigResolver = class {
228
230
  }
229
231
  };
230
232
 
231
- // src/core/ConfigValidator.ts
232
- var ConfigValidator = class {
233
- /**
234
- * Validates the entire RagConfig object.
235
- * Returns an array of validation errors. Empty array = valid config.
236
- */
237
- static validate(config) {
238
- const errors = [];
239
- if (!config.projectId) {
240
- errors.push({
241
- field: "projectId",
242
- message: "projectId is required",
243
- severity: "error"
244
- });
245
- }
246
- errors.push(...this.validateVectorDbConfig(config.vectorDb));
247
- errors.push(...this.validateLLMConfig(config.llm));
248
- if (config.embedding) {
249
- errors.push(...this.validateEmbeddingConfig(config.embedding));
250
- } else if (config.llm.provider === "anthropic") {
251
- errors.push({
252
- field: "embedding",
253
- message: "Embedding config is required when using Anthropic LLM",
254
- suggestion: 'Provide embedding config with provider (e.g., "openai" or "ollama")',
255
- severity: "error"
256
- });
257
- }
258
- if (config.ui) {
259
- errors.push(...this.validateUIConfig(config.ui));
260
- }
261
- if (config.rag) {
262
- errors.push(...this.validateRAGConfig(config.rag));
263
- }
264
- return errors;
265
- }
266
- static validateVectorDbConfig(config) {
267
- const errors = [];
268
- if (!config.provider) {
269
- errors.push({
270
- field: "vectorDb.provider",
271
- message: "Vector database provider is required",
272
- severity: "error"
273
- });
274
- return errors;
275
- }
276
- if (!config.indexName) {
277
- errors.push({
278
- field: "vectorDb.indexName",
279
- message: "Vector database index name is required",
280
- severity: "error"
281
- });
282
- }
283
- switch (config.provider) {
284
- case "pinecone":
285
- errors.push(...this.validatePineconeConfig(config));
286
- break;
287
- case "pgvector":
288
- case "postgresql":
289
- errors.push(...this.validatePostgresConfig(config));
290
- break;
291
- case "mongodb":
292
- errors.push(...this.validateMongoDBConfig(config));
293
- break;
294
- case "milvus":
295
- errors.push(...this.validateMilvusConfig(config));
296
- break;
297
- case "qdrant":
298
- errors.push(...this.validateQdrantConfig(config));
299
- break;
300
- case "chromadb":
301
- errors.push(...this.validateChromaDBConfig(config));
302
- break;
303
- case "redis":
304
- errors.push(...this.validateRedisConfig(config));
305
- break;
306
- case "weaviate":
307
- errors.push(...this.validateWeaviateConfig(config));
308
- break;
309
- case "universal_rest":
310
- case "rest":
311
- errors.push(...this.validateRestConfig(config));
312
- break;
313
- }
314
- return errors;
315
- }
316
- /**
317
- * Pinecone — only needs apiKey (environment was removed in SDK v7+).
318
- * Options key: { apiKey: string }
319
- */
320
- static validatePineconeConfig(config) {
321
- const errors = [];
322
- const opts = config.options;
323
- if (!opts.apiKey) {
324
- errors.push({
325
- field: "vectorDb.options.apiKey",
326
- message: "Pinecone API key is required",
327
- suggestion: "Set PINECONE_API_KEY environment variable",
328
- severity: "error"
329
- });
330
- }
331
- return errors;
332
- }
333
- /**
334
- * PostgreSQL / pgvector — needs a connection string.
335
- * Options key: { connectionString: string }
336
- */
337
- static validatePostgresConfig(config) {
338
- const errors = [];
339
- const opts = config.options;
340
- if (!opts.connectionString) {
341
- errors.push({
342
- field: "vectorDb.options.connectionString",
343
- message: "PostgreSQL connection string is required",
344
- suggestion: "Set PGVECTOR_CONNECTION_STRING environment variable",
345
- severity: "error"
346
- });
347
- }
348
- if (opts.tables && typeof opts.tables !== "string" && !Array.isArray(opts.tables)) {
349
- errors.push({
350
- field: "vectorDb.options.tables",
351
- message: "PostgreSQL tables must be a string or a string array",
352
- severity: "error"
353
- });
354
- }
355
- if (opts.searchFields && typeof opts.searchFields !== "string" && !Array.isArray(opts.searchFields)) {
356
- errors.push({
357
- field: "vectorDb.options.searchFields",
358
- message: "PostgreSQL searchFields must be a string or a string array",
359
- severity: "error"
360
- });
361
- }
362
- return errors;
363
- }
364
- /**
365
- * MongoDB — needs uri, database, and collection.
366
- * Options keys: { uri: string, database: string, collection: string }
367
- */
368
- static validateMongoDBConfig(config) {
369
- const errors = [];
370
- const opts = config.options;
371
- if (!opts.uri) {
372
- errors.push({
373
- field: "vectorDb.options.uri",
374
- message: "MongoDB connection URI is required",
375
- suggestion: "Set MONGODB_URI environment variable",
376
- severity: "error"
377
- });
378
- }
379
- if (!opts.database) {
380
- errors.push({
381
- field: "vectorDb.options.database",
382
- message: "MongoDB database name is required",
383
- suggestion: "Set MONGODB_DB environment variable",
384
- severity: "error"
385
- });
386
- }
387
- if (!opts.collection) {
388
- errors.push({
389
- field: "vectorDb.options.collection",
390
- message: "MongoDB collection name is required",
391
- suggestion: "Set MONGODB_COLLECTION environment variable",
392
- severity: "error"
393
- });
394
- }
395
- return errors;
396
- }
397
- /**
398
- * Milvus — accepts baseUrl OR uri (preferred) OR host+port.
399
- * MilvusProvider reads opts.baseUrl ?? opts.uri.
400
- * Options keys: { baseUrl?: string, uri?: string, host?: string, port?: number }
401
- */
402
- static validateMilvusConfig(config) {
403
- const errors = [];
404
- const opts = config.options;
405
- const hasBaseUrl = Boolean(opts.baseUrl || opts.uri);
406
- const hasHostPort = Boolean(opts.host && opts.port);
407
- if (!hasBaseUrl && !hasHostPort) {
408
- errors.push({
409
- field: "vectorDb.options.baseUrl",
410
- message: 'Milvus connection is required: provide baseUrl (e.g., "http://localhost:19530") or host + port',
411
- suggestion: "Set MILVUS_URL environment variable",
412
- severity: "error"
413
- });
414
- }
415
- return errors;
416
- }
417
- /**
418
- * Qdrant — needs baseUrl (or url alias).
419
- * Options keys: { baseUrl: string, apiKey?: string }
420
- */
421
- static validateQdrantConfig(config) {
422
- const errors = [];
423
- const opts = config.options;
424
- if (!opts.baseUrl && !opts.url) {
425
- errors.push({
426
- field: "vectorDb.options.baseUrl",
427
- message: "Qdrant base URL is required",
428
- suggestion: 'Set QDRANT_URL environment variable (e.g., "http://localhost:6333")',
429
- severity: "error"
430
- });
431
- }
432
- return errors;
433
- }
434
- /**
435
- * ChromaDB — accepts baseUrl OR host.
436
- * ChromaDBProvider reads opts.baseUrl.
437
- * Options keys: { baseUrl?: string, host?: string, port?: number }
438
- */
439
- static validateChromaDBConfig(config) {
440
- const errors = [];
441
- const opts = config.options;
442
- if (!opts.baseUrl && !opts.host) {
443
- errors.push({
444
- field: "vectorDb.options.baseUrl",
445
- message: 'ChromaDB connection is required: provide baseUrl (e.g., "http://localhost:8000") or host',
446
- suggestion: "Set CHROMADB_URL environment variable",
447
- severity: "error"
448
- });
449
- }
450
- return errors;
451
- }
452
- /**
453
- * Redis — accepts baseUrl OR url OR host+port.
454
- * RedisProvider reads opts.baseUrl.
455
- * Options keys: { baseUrl?: string, url?: string, host?: string, port?: number, apiKey?: string }
456
- */
457
- static validateRedisConfig(config) {
458
- const errors = [];
459
- const opts = config.options;
460
- const hasUrl = Boolean(opts.baseUrl || opts.url);
461
- const hasHostPort = Boolean(opts.host && opts.port);
462
- if (!hasUrl && !hasHostPort) {
463
- errors.push({
464
- field: "vectorDb.options.baseUrl",
465
- message: "Redis connection is required: provide baseUrl/url or host + port",
466
- suggestion: "Set REDIS_URL environment variable",
467
- severity: "error"
468
- });
469
- }
470
- return errors;
471
- }
472
- /**
473
- * Weaviate — accepts baseUrl OR url.
474
- * WeaviateProvider reads opts.baseUrl.
475
- * Options keys: { baseUrl?: string, url?: string, apiKey?: string }
476
- */
477
- static validateWeaviateConfig(config) {
478
- const errors = [];
479
- const opts = config.options;
480
- if (!opts.baseUrl && !opts.url) {
481
- errors.push({
482
- field: "vectorDb.options.baseUrl",
483
- message: "Weaviate instance URL is required",
484
- suggestion: "Set WEAVIATE_URL environment variable",
485
- severity: "error"
486
- });
487
- }
488
- return errors;
489
- }
490
- /**
491
- * Universal REST / custom REST adapter.
492
- * Options key: { baseUrl: string }
493
- */
494
- static validateRestConfig(config) {
495
- const errors = [];
496
- const opts = config.options;
497
- if (!opts.baseUrl) {
498
- errors.push({
499
- field: "vectorDb.options.baseUrl",
500
- message: "REST API base URL is required",
501
- suggestion: "Set VECTOR_BASE_URL environment variable",
502
- severity: "error"
503
- });
504
- }
505
- return errors;
233
+ // src/llm/providers/OpenAIProvider.ts
234
+ import OpenAI from "openai";
235
+ var OpenAIProvider = class {
236
+ constructor(llmConfig, embeddingConfig) {
237
+ if (!llmConfig.apiKey) throw new Error("[OpenAIProvider] llmConfig.apiKey is required");
238
+ this.client = new OpenAI({ apiKey: llmConfig.apiKey });
239
+ this.llmConfig = llmConfig;
240
+ this.embeddingConfig = embeddingConfig;
506
241
  }
507
- static validateLLMConfig(config) {
508
- var _a;
509
- const errors = [];
510
- if (!config.provider) {
511
- errors.push({
512
- field: "llm.provider",
513
- message: "LLM provider is required",
514
- severity: "error"
515
- });
516
- return errors;
517
- }
518
- if (!config.model) {
519
- errors.push({
520
- field: "llm.model",
521
- message: "LLM model name is required",
522
- suggestion: `e.g., "gpt-4o" for OpenAI, "claude-3-opus" for Anthropic`,
523
- severity: "error"
524
- });
525
- }
526
- switch (config.provider) {
527
- case "openai":
242
+ static getValidator() {
243
+ return {
244
+ validate(config) {
245
+ const errors = [];
246
+ const isEmbedding = config.provider === "openai" && "dimensions" in config;
247
+ const prefix = isEmbedding ? "embedding" : "llm";
528
248
  if (!config.apiKey) {
529
249
  errors.push({
530
- field: "llm.apiKey",
250
+ field: `${prefix}.apiKey`,
531
251
  message: "OpenAI API key is required",
532
252
  suggestion: "Set OPENAI_API_KEY environment variable",
533
253
  severity: "error"
534
254
  });
535
255
  }
536
- break;
537
- case "anthropic":
538
- if (!config.apiKey) {
539
- errors.push({
540
- field: "llm.apiKey",
541
- message: "Anthropic API key is required",
542
- suggestion: "Set ANTHROPIC_API_KEY environment variable",
543
- severity: "error"
544
- });
545
- }
546
- break;
547
- case "gemini":
548
- if (!config.apiKey) {
549
- errors.push({
550
- field: "llm.apiKey",
551
- message: "Gemini API key is required",
552
- suggestion: "Set GEMINI_API_KEY environment variable",
553
- severity: "error"
554
- });
555
- }
556
- break;
557
- case "ollama":
558
- if (!config.baseUrl) {
256
+ if (!config.model) {
559
257
  errors.push({
560
- field: "llm.baseUrl",
561
- message: "Ollama base URL is required",
562
- suggestion: 'Set LLM_BASE_URL environment variable (e.g., "http://localhost:11434")',
258
+ field: `${prefix}.model`,
259
+ message: "OpenAI model name is required",
260
+ suggestion: isEmbedding ? 'e.g., "text-embedding-3-small"' : 'e.g., "gpt-4o"',
563
261
  severity: "error"
564
262
  });
565
263
  }
566
- break;
567
- case "rest":
568
- case "universal_rest":
569
- if (!config.baseUrl && !((_a = config.options) == null ? void 0 : _a.baseUrl)) {
570
- errors.push({
571
- field: "llm.baseUrl",
572
- message: "REST API base URL is required",
573
- suggestion: "Set LLM_BASE_URL environment variable",
574
- severity: "error"
575
- });
264
+ return errors;
265
+ }
266
+ };
267
+ }
268
+ static getHealthChecker() {
269
+ return {
270
+ async check(config) {
271
+ const timestamp = Date.now();
272
+ const apiKey = config.apiKey;
273
+ const modelName = config.model;
274
+ try {
275
+ const OpenAI2 = await import("openai");
276
+ const client = new OpenAI2.default({ apiKey });
277
+ const models = await client.models.list();
278
+ const hasModel = models.data.some((m) => m.id === modelName);
279
+ return {
280
+ healthy: true,
281
+ provider: "openai",
282
+ capabilities: { model: modelName, available: hasModel, totalModels: models.data.length },
283
+ timestamp
284
+ };
285
+ } catch (error) {
286
+ return {
287
+ healthy: false,
288
+ provider: "openai",
289
+ error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
290
+ timestamp
291
+ };
576
292
  }
577
- break;
578
- }
579
- if (config.temperature !== void 0) {
580
- if (config.temperature < 0 || config.temperature > 2) {
581
- errors.push({
582
- field: "llm.temperature",
583
- message: "Temperature must be between 0 and 2",
584
- severity: "error"
585
- });
586
293
  }
587
- }
588
- if (config.maxTokens !== void 0 && config.maxTokens <= 0) {
589
- errors.push({
590
- field: "llm.maxTokens",
591
- message: "maxTokens must be greater than 0",
592
- severity: "error"
593
- });
594
- }
595
- return errors;
596
- }
597
- static validateEmbeddingConfig(config) {
598
- const errors = [];
599
- if (!config.provider) {
600
- errors.push({
601
- field: "embedding.provider",
602
- message: "Embedding provider is required",
603
- severity: "error"
604
- });
605
- return errors;
606
- }
607
- if (!config.model) {
608
- errors.push({
609
- field: "embedding.model",
610
- message: "Embedding model name is required",
611
- suggestion: 'e.g., "text-embedding-3-small" for OpenAI, "nomic-embed-text" for Ollama',
612
- severity: "error"
613
- });
614
- }
615
- if (config.provider === "openai" && !config.apiKey) {
616
- errors.push({
617
- field: "embedding.apiKey",
618
- message: "OpenAI API key is required for embedding",
619
- suggestion: "Set OPENAI_API_KEY environment variable",
620
- severity: "error"
621
- });
622
- }
623
- if (config.provider === "gemini" && !config.apiKey) {
624
- errors.push({
625
- field: "embedding.apiKey",
626
- message: "Gemini API key is required for embedding",
627
- suggestion: "Set GEMINI_API_KEY environment variable",
628
- severity: "error"
629
- });
630
- }
631
- if (config.provider === "ollama" && !config.baseUrl) {
632
- errors.push({
633
- field: "embedding.baseUrl",
634
- message: "Ollama base URL is required for embedding",
635
- suggestion: "Set EMBEDDING_BASE_URL environment variable",
636
- severity: "error"
637
- });
638
- }
639
- if (config.dimensions !== void 0 && config.dimensions <= 0) {
640
- errors.push({
641
- field: "embedding.dimensions",
642
- message: "Embedding dimensions must be greater than 0",
643
- severity: "error"
644
- });
645
- }
646
- return errors;
647
- }
648
- static validateUIConfig(config) {
649
- const errors = [];
650
- if (config.primaryColor) {
651
- if (!this.isValidCSSColor(config.primaryColor)) {
652
- errors.push({
653
- field: "ui.primaryColor",
654
- message: "Invalid CSS color format",
655
- suggestion: "Use valid CSS colors: hex (#FF0000), rgb, hsl, or named colors",
656
- severity: "warning"
657
- });
658
- }
659
- }
660
- if (config.borderRadius) {
661
- if (!UI_BORDER_RADIUS_OPTIONS.includes(config.borderRadius)) {
662
- errors.push({
663
- field: "ui.borderRadius",
664
- message: `borderRadius must be one of: ${UI_BORDER_RADIUS_OPTIONS.join(", ")}`,
665
- severity: "warning"
666
- });
667
- }
668
- }
669
- if (config.visualStyle) {
670
- if (!UI_VISUAL_STYLES.includes(config.visualStyle)) {
671
- errors.push({
672
- field: "ui.visualStyle",
673
- message: `visualStyle must be one of: ${UI_VISUAL_STYLES.join(", ")}`,
674
- severity: "warning"
675
- });
676
- }
677
- }
678
- return errors;
679
- }
680
- static validateRAGConfig(config) {
681
- const errors = [];
682
- if (config.topK !== void 0) {
683
- if (typeof config.topK !== "number" || config.topK <= 0) {
684
- errors.push({
685
- field: "rag.topK",
686
- message: "topK must be a positive integer",
687
- severity: "error"
688
- });
689
- }
690
- }
691
- if (config.scoreThreshold !== void 0) {
692
- if (typeof config.scoreThreshold !== "number" || config.scoreThreshold < 0 || config.scoreThreshold > 1) {
693
- errors.push({
694
- field: "rag.scoreThreshold",
695
- message: "scoreThreshold must be between 0 and 1",
696
- severity: "error"
697
- });
698
- }
699
- }
700
- if (config.chunkSize !== void 0) {
701
- if (typeof config.chunkSize !== "number" || config.chunkSize <= 0) {
702
- errors.push({
703
- field: "rag.chunkSize",
704
- message: "chunkSize must be a positive integer",
705
- severity: "error"
706
- });
707
- }
708
- }
709
- if (config.chunkOverlap !== void 0) {
710
- if (typeof config.chunkOverlap !== "number" || config.chunkOverlap < 0) {
711
- errors.push({
712
- field: "rag.chunkOverlap",
713
- message: "chunkOverlap must be a non-negative integer",
714
- severity: "error"
715
- });
716
- }
717
- }
718
- return errors;
719
- }
720
- static isValidCSSColor(color) {
721
- const hexRegex = /^#([0-9a-f]{3}){1,2}$/i;
722
- const rgbRegex = /^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/i;
723
- const hslRegex = /^hsla?\((\d+),\s*(\d+)%,\s*(\d+)%(?:,\s*(\d+(?:\.\d+)?))?\)$/i;
724
- const namedColors = ["red", "blue", "green", "black", "white", "transparent"];
725
- return hexRegex.test(color) || rgbRegex.test(color) || hslRegex.test(color) || namedColors.includes(color.toLowerCase());
726
- }
727
- /**
728
- * Throws if there are error-level validation issues.
729
- * Logs warnings to console.
730
- */
731
- static validateAndThrow(config) {
732
- const errors = this.validate(config);
733
- const errorItems = errors.filter((e) => e.severity === "error");
734
- const warnings = errors.filter((e) => e.severity === "warning");
735
- if (warnings.length > 0) {
736
- console.warn("[ConfigValidator] Configuration warnings:");
737
- warnings.forEach((w) => {
738
- console.warn(` ${w.field}: ${w.message}`);
739
- if (w.suggestion) console.warn(` Suggestion: ${w.suggestion}`);
740
- });
741
- }
742
- if (errorItems.length > 0) {
743
- const message = errorItems.map((e) => `${e.field}: ${e.message}${e.suggestion ? ` (${e.suggestion})` : ""}`).join("\n ");
744
- throw new Error(`[ConfigValidator] Configuration validation failed:
745
- ${message}`);
746
- }
747
- }
748
- };
749
-
750
- // src/rag/DocumentChunker.ts
751
- var DocumentChunker = class {
752
- constructor(chunkSize = 1e3, chunkOverlap = 200, separators = ["\n\n", "\n", " ", ""]) {
753
- this.chunkSize = chunkSize;
754
- this.chunkOverlap = chunkOverlap;
755
- this.separators = separators;
756
- }
757
- /**
758
- * Split a single text string into overlapping chunks using a recursive strategy.
759
- */
760
- chunk(text, options = {}) {
761
- const {
762
- chunkSize = this.chunkSize,
763
- chunkOverlap = this.chunkOverlap,
764
- docId = `doc_${Date.now()}`,
765
- metadata = {},
766
- separators = this.separators
767
- } = options;
768
- const finalChunks = [];
769
- const splits = this.recursiveSplit(text, separators, chunkSize);
770
- let currentChunk = [];
771
- let currentLength = 0;
772
- let chunkIndex = 0;
773
- for (const split of splits) {
774
- if (currentLength + split.length > chunkSize && currentChunk.length > 0) {
775
- finalChunks.push({
776
- id: `${docId}_chunk_${chunkIndex++}`,
777
- content: currentChunk.join("").trim(),
778
- metadata: __spreadProps(__spreadValues({}, metadata), { docId, chunkIndex: chunkIndex - 1 })
779
- });
780
- const overlapItems = [];
781
- let overlapLen = 0;
782
- for (let i = currentChunk.length - 1; i >= 0; i--) {
783
- if (overlapLen + currentChunk[i].length <= chunkOverlap) {
784
- overlapItems.unshift(currentChunk[i]);
785
- overlapLen += currentChunk[i].length;
786
- } else {
787
- break;
788
- }
789
- }
790
- currentChunk = overlapItems;
791
- currentLength = overlapLen;
792
- }
793
- currentChunk.push(split);
794
- currentLength += split.length;
795
- }
796
- if (currentChunk.length > 0) {
797
- finalChunks.push({
798
- id: `${docId}_chunk_${chunkIndex}`,
799
- content: currentChunk.join("").trim(),
800
- metadata: __spreadProps(__spreadValues({}, metadata), { docId, chunkIndex })
801
- });
802
- }
803
- return finalChunks;
804
- }
805
- /**
806
- * Recursively split text based on separators.
807
- */
808
- recursiveSplit(text, separators, chunkSize) {
809
- const finalSplits = [];
810
- let separator = separators[separators.length - 1];
811
- let nextSeparators = [];
812
- for (let i = 0; i < separators.length; i++) {
813
- if (text.includes(separators[i])) {
814
- separator = separators[i];
815
- nextSeparators = separators.slice(i + 1);
816
- break;
817
- }
818
- }
819
- const parts = text.split(separator);
820
- for (const part of parts) {
821
- if (part.length <= chunkSize) {
822
- finalSplits.push(part + separator);
823
- } else if (nextSeparators.length > 0) {
824
- finalSplits.push(...this.recursiveSplit(part, nextSeparators, chunkSize));
825
- } else {
826
- finalSplits.push(part);
827
- }
828
- }
829
- return finalSplits;
830
- }
831
- /**
832
- * Chunk multiple documents at once.
833
- */
834
- chunkMany(documents) {
835
- return documents.flatMap(
836
- (doc) => this.chunk(doc.content, { docId: doc.docId, metadata: doc.metadata })
837
- );
838
- }
839
- };
840
-
841
- // src/rag/EntityExtractor.ts
842
- var EntityExtractor = class {
843
- constructor(llm) {
844
- this.llm = llm;
845
- }
846
- /**
847
- * Extract nodes and edges from a text chunk.
848
- */
849
- async extract(text) {
850
- const prompt = `
851
- Extract entities and relationships from the following text.
852
- Format the output as JSON with two keys: "nodes" (array of {id, label, properties}) and "edges" (array of {source, target, type, properties}).
853
- Use the same ID for the same entity.
854
-
855
- Text:
856
- "${text}"
857
-
858
- Output JSON:
859
- `;
860
- const response = await this.llm.chat([
861
- { role: "system", content: "You are an expert at knowledge graph extraction. Respond only with valid JSON." },
862
- { role: "user", content: prompt }
863
- ], "");
864
- try {
865
- const cleanJson = response.replace(/```json\n?|\n?```/g, "").trim();
866
- return JSON.parse(cleanJson);
867
- } catch (error) {
868
- console.warn("[EntityExtractor] Failed to parse LLM response as JSON:", response);
869
- return { nodes: [], edges: [] };
870
- }
871
- }
872
- };
873
-
874
- // src/llm/providers/OpenAIProvider.ts
875
- import OpenAI from "openai";
876
- var OpenAIProvider = class {
877
- constructor(llmConfig, embeddingConfig) {
878
- if (!llmConfig.apiKey) throw new Error("[OpenAIProvider] llmConfig.apiKey is required");
879
- this.client = new OpenAI({ apiKey: llmConfig.apiKey });
880
- this.llmConfig = llmConfig;
881
- this.embeddingConfig = embeddingConfig;
294
+ };
882
295
  }
883
296
  async chat(messages, context, options) {
884
297
  var _a, _b, _c, _d, _e, _f, _g, _h;
@@ -941,6 +354,56 @@ var AnthropicProvider = class {
941
354
  this.llmConfig = llmConfig;
942
355
  this.embeddingConfig = embeddingConfig;
943
356
  }
357
+ static getValidator() {
358
+ return {
359
+ validate(config) {
360
+ const errors = [];
361
+ if (!config.apiKey) {
362
+ errors.push({
363
+ field: "llm.apiKey",
364
+ message: "Anthropic API key is required",
365
+ suggestion: "Set ANTHROPIC_API_KEY environment variable",
366
+ severity: "error"
367
+ });
368
+ }
369
+ if (!config.model) {
370
+ errors.push({
371
+ field: "llm.model",
372
+ message: "Anthropic model name is required",
373
+ suggestion: 'e.g., "claude-3-5-sonnet-20241022"',
374
+ severity: "error"
375
+ });
376
+ }
377
+ return errors;
378
+ }
379
+ };
380
+ }
381
+ static getHealthChecker() {
382
+ return {
383
+ async check(config) {
384
+ const timestamp = Date.now();
385
+ const apiKey = config.apiKey;
386
+ const modelName = config.model;
387
+ try {
388
+ const { default: Anthropic2 } = await import("@anthropic-ai/sdk");
389
+ const client = new Anthropic2({ apiKey });
390
+ await client.messages.create({
391
+ model: modelName,
392
+ max_tokens: 10,
393
+ messages: [{ role: "user", content: "ping" }]
394
+ });
395
+ return { healthy: true, provider: "anthropic", capabilities: { model: modelName }, timestamp };
396
+ } catch (error) {
397
+ return {
398
+ healthy: false,
399
+ provider: "anthropic",
400
+ error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
401
+ timestamp
402
+ };
403
+ }
404
+ }
405
+ };
406
+ }
944
407
  async chat(messages, context, options) {
945
408
  var _a, _b, _c;
946
409
  const systemPrompt = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Use the context below to answer the user's question accurately.
@@ -964,11 +427,6 @@ ${context}`;
964
427
  const block = response.content[0];
965
428
  return block.type === "text" ? block.text : "";
966
429
  }
967
- /**
968
- * Anthropic does not offer an embedding API.
969
- * This method throws with a clear error so developers know to configure
970
- * a separate embedding provider (OpenAI or Ollama).
971
- */
972
430
  async embed(text, options) {
973
431
  void text;
974
432
  void options;
@@ -1004,22 +462,66 @@ var OllamaProvider = class {
1004
462
  this.llmConfig = llmConfig;
1005
463
  this.embeddingConfig = embeddingConfig;
1006
464
  }
465
+ static getValidator() {
466
+ return {
467
+ validate(config) {
468
+ const errors = [];
469
+ if (!config.model) {
470
+ const isEmbedding = config.provider === "ollama" && "dimensions" in config;
471
+ const prefix = isEmbedding ? "embedding" : "llm";
472
+ errors.push({
473
+ field: `${prefix}.model`,
474
+ message: "Ollama model name is required",
475
+ suggestion: 'e.g., "llama3" or "mistral"',
476
+ severity: "error"
477
+ });
478
+ }
479
+ return errors;
480
+ }
481
+ };
482
+ }
483
+ static getHealthChecker() {
484
+ return {
485
+ async check(config) {
486
+ const timestamp = Date.now();
487
+ const baseUrl = config.baseUrl || "http://localhost:11434";
488
+ const modelName = config.model;
489
+ try {
490
+ const axios3 = (await import("axios")).default;
491
+ const { data } = await axios3.get(`${baseUrl}/api/tags`);
492
+ const models = data.models || [];
493
+ const hasModel = models.some((m) => m.name === modelName || m.name.startsWith(`${modelName}:`));
494
+ return {
495
+ healthy: true,
496
+ provider: "ollama",
497
+ capabilities: {
498
+ baseUrl,
499
+ model: modelName,
500
+ available: hasModel,
501
+ totalModels: models.length
502
+ },
503
+ timestamp
504
+ };
505
+ } catch (e) {
506
+ return {
507
+ healthy: false,
508
+ provider: "ollama",
509
+ error: `Ollama server not reachable at ${baseUrl}. Is it running?`,
510
+ timestamp
511
+ };
512
+ }
513
+ }
514
+ };
515
+ }
1007
516
  async chat(messages, context, options) {
1008
- var _a, _b, _c, _d, _e;
1009
- const systemPrompt = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Use the provided context to answer the user's question.
1010
-
1011
- Context:
1012
- ${context}`;
1013
- const system = systemPrompt.includes("{{context}}") ? systemPrompt.replace("{{context}}", context) : `${systemPrompt}
1014
-
1015
- Context:
1016
- ${context}`;
517
+ var _a, _b, _c, _d;
518
+ const system = this.buildSystemPrompt(context);
1017
519
  const { data } = await this.http.post("/api/chat", {
1018
520
  model: this.llmConfig.model,
1019
521
  stream: false,
1020
522
  options: {
1021
- temperature: (_c = (_b = options == null ? void 0 : options.temperature) != null ? _b : this.llmConfig.temperature) != null ? _c : 0.7,
1022
- num_predict: (_e = (_d = options == null ? void 0 : options.maxTokens) != null ? _d : this.llmConfig.maxTokens) != null ? _e : 1024
523
+ temperature: (_b = (_a = options == null ? void 0 : options.temperature) != null ? _a : this.llmConfig.temperature) != null ? _b : 0.7,
524
+ num_predict: (_d = (_c = options == null ? void 0 : options.maxTokens) != null ? _c : this.llmConfig.maxTokens) != null ? _d : 1024
1023
525
  },
1024
526
  messages: [
1025
527
  { role: "system", content: system },
@@ -1028,14 +530,68 @@ ${context}`;
1028
530
  });
1029
531
  return data.message.content;
1030
532
  }
1031
- async embed(text, options) {
1032
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
1033
- 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";
1034
- const baseURL = (_f = (_e = (_d = this.embeddingConfig) == null ? void 0 : _d.baseUrl) != null ? _e : this.llmConfig.baseUrl) != null ? _f : "http://localhost:11434";
1035
- const client = baseURL !== ((_g = this.llmConfig.baseUrl) != null ? _g : "http://localhost:11434") ? axios.create({ baseURL, timeout: 6e4 }) : this.http;
1036
- let prompt = text;
1037
- const queryPrefix = (_i = (_h = this.embeddingConfig) == null ? void 0 : _h.queryPrefix) != null ? _i : model.includes("nomic") ? "search_query: " : "";
1038
- const docPrefix = (_k = (_j = this.embeddingConfig) == null ? void 0 : _j.documentPrefix) != null ? _k : model.includes("nomic") ? "search_document: " : "";
533
+ chatStream(messages, context, options) {
534
+ return __asyncGenerator(this, null, function* () {
535
+ var _a, _b, _c, _d, _e;
536
+ const system = this.buildSystemPrompt(context);
537
+ const response = yield new __await(this.http.post("/api/chat", {
538
+ model: this.llmConfig.model,
539
+ stream: true,
540
+ options: {
541
+ temperature: (_b = (_a = options == null ? void 0 : options.temperature) != null ? _a : this.llmConfig.temperature) != null ? _b : 0.7,
542
+ num_predict: (_d = (_c = options == null ? void 0 : options.maxTokens) != null ? _c : this.llmConfig.maxTokens) != null ? _d : 1024
543
+ },
544
+ messages: [
545
+ { role: "system", content: system },
546
+ ...messages.map((m) => ({ role: m.role, content: m.content }))
547
+ ]
548
+ }, { responseType: "stream" }));
549
+ try {
550
+ for (var iter = __forAwait(response.data), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
551
+ const chunk = temp.value;
552
+ const lines = chunk.toString().split("\n").filter(Boolean);
553
+ for (const line of lines) {
554
+ try {
555
+ const json = JSON.parse(line);
556
+ if ((_e = json.message) == null ? void 0 : _e.content) {
557
+ yield json.message.content;
558
+ }
559
+ if (json.done) return;
560
+ } catch (e) {
561
+ }
562
+ }
563
+ }
564
+ } catch (temp) {
565
+ error = [temp];
566
+ } finally {
567
+ try {
568
+ more && (temp = iter.return) && (yield new __await(temp.call(iter)));
569
+ } finally {
570
+ if (error)
571
+ throw error[0];
572
+ }
573
+ }
574
+ });
575
+ }
576
+ buildSystemPrompt(context) {
577
+ var _a;
578
+ const systemPrompt = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Use the provided context to answer the user's question.
579
+
580
+ Context:
581
+ ${context}`;
582
+ return systemPrompt.includes("{{context}}") ? systemPrompt.replace("{{context}}", context) : `${systemPrompt}
583
+
584
+ Context:
585
+ ${context}`;
586
+ }
587
+ async embed(text, options) {
588
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
589
+ 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";
590
+ const baseURL = (_f = (_e = (_d = this.embeddingConfig) == null ? void 0 : _d.baseUrl) != null ? _e : this.llmConfig.baseUrl) != null ? _f : "http://localhost:11434";
591
+ const client = baseURL !== ((_g = this.llmConfig.baseUrl) != null ? _g : "http://localhost:11434") ? axios.create({ baseURL, timeout: 6e4 }) : this.http;
592
+ let prompt = text;
593
+ const queryPrefix = (_i = (_h = this.embeddingConfig) == null ? void 0 : _h.queryPrefix) != null ? _i : model.includes("nomic") ? "search_query: " : "";
594
+ const docPrefix = (_k = (_j = this.embeddingConfig) == null ? void 0 : _j.documentPrefix) != null ? _k : model.includes("nomic") ? "search_document: " : "";
1039
595
  if ((options == null ? void 0 : options.taskType) === "query" && queryPrefix) {
1040
596
  if (!prompt.startsWith(queryPrefix)) {
1041
597
  prompt = `${queryPrefix}${text}`;
@@ -1084,6 +640,52 @@ var GeminiProvider = class {
1084
640
  });
1085
641
  }
1086
642
  }
643
+ static getValidator() {
644
+ return {
645
+ validate(config) {
646
+ const errors = [];
647
+ if (!config.apiKey && !process.env.GOOGLE_GENAI_API_KEY) {
648
+ errors.push({
649
+ field: "llm.apiKey",
650
+ message: "Gemini API key is required",
651
+ suggestion: "Set GOOGLE_GENAI_API_KEY environment variable or provide in config",
652
+ severity: "error"
653
+ });
654
+ }
655
+ if (!config.model) {
656
+ errors.push({ field: "llm.model", message: "Gemini model name is required", severity: "error" });
657
+ }
658
+ return errors;
659
+ }
660
+ };
661
+ }
662
+ static getHealthChecker() {
663
+ return {
664
+ async check(config) {
665
+ const timestamp = Date.now();
666
+ const apiKey = config.apiKey || process.env.GOOGLE_GENAI_API_KEY || "";
667
+ const modelName = config.model;
668
+ try {
669
+ const { GoogleGenAI: GoogleGenAI2 } = await import("@google/genai");
670
+ const genAI = new GoogleGenAI2({ apiKey });
671
+ await genAI.models.get({ model: modelName });
672
+ return {
673
+ healthy: true,
674
+ provider: "gemini",
675
+ capabilities: { model: modelName },
676
+ timestamp
677
+ };
678
+ } catch (error) {
679
+ return {
680
+ healthy: false,
681
+ provider: "gemini",
682
+ error: error instanceof Error ? error.message : String(error),
683
+ timestamp
684
+ };
685
+ }
686
+ }
687
+ };
688
+ }
1087
689
  sanitizeModel(model) {
1088
690
  if (!model) return model;
1089
691
  return model.split(":")[0];
@@ -1325,189 +927,607 @@ ${context != null ? context : "None"}` },
1325
927
  if (result === void 0) {
1326
928
  throw new Error(`[UniversalLLMAdapter] Could not extract text from path '${extractPath}' in response.`);
1327
929
  }
1328
- return String(result);
1329
- }
1330
- async embed(text) {
1331
- var _a, _b;
1332
- const path = (_a = this.opts.embedPath) != null ? _a : "/embeddings";
1333
- let payload;
1334
- if (this.opts.embedPayloadTemplate) {
1335
- payload = buildPayload(this.opts.embedPayloadTemplate, {
1336
- model: this.model,
1337
- input: text
930
+ return String(result);
931
+ }
932
+ async embed(text) {
933
+ var _a, _b;
934
+ const path = (_a = this.opts.embedPath) != null ? _a : "/embeddings";
935
+ let payload;
936
+ if (this.opts.embedPayloadTemplate) {
937
+ payload = buildPayload(this.opts.embedPayloadTemplate, {
938
+ model: this.model,
939
+ input: text
940
+ });
941
+ } else {
942
+ payload = {
943
+ model: this.model,
944
+ input: text
945
+ };
946
+ }
947
+ const { data } = await this.http.post(path, payload);
948
+ const extractPath = (_b = this.opts.embedExtractPath) != null ? _b : "data[0].embedding";
949
+ const vector = resolvePath(data, extractPath);
950
+ if (!Array.isArray(vector)) {
951
+ throw new Error(`[UniversalLLMAdapter] Expected a number array at '${extractPath}' for embeddings.`);
952
+ }
953
+ return vector;
954
+ }
955
+ async batchEmbed(texts) {
956
+ const vectors = [];
957
+ for (const text of texts) {
958
+ vectors.push(await this.embed(text));
959
+ }
960
+ return vectors;
961
+ }
962
+ async ping() {
963
+ try {
964
+ if (this.opts.pingPath) {
965
+ await this.http.get(this.opts.pingPath);
966
+ }
967
+ return true;
968
+ } catch (err) {
969
+ console.error("[UniversalLLMAdapter] Ping failed:", err);
970
+ return false;
971
+ }
972
+ }
973
+ };
974
+
975
+ // src/llm/LLMFactory.ts
976
+ var LLMFactory = class _LLMFactory {
977
+ static create(llmConfig, embeddingConfig) {
978
+ var _a;
979
+ switch (llmConfig.provider) {
980
+ case "openai":
981
+ return new OpenAIProvider(llmConfig, embeddingConfig);
982
+ case "anthropic":
983
+ return new AnthropicProvider(llmConfig, embeddingConfig);
984
+ case "ollama":
985
+ return new OllamaProvider(llmConfig, embeddingConfig);
986
+ case "gemini":
987
+ return new GeminiProvider(llmConfig, embeddingConfig);
988
+ case "rest":
989
+ case "universal_rest":
990
+ case "custom":
991
+ return new UniversalLLMAdapter(llmConfig);
992
+ default:
993
+ if (llmConfig.baseUrl || ((_a = llmConfig.options) == null ? void 0 : _a.baseUrl)) {
994
+ return new UniversalLLMAdapter(llmConfig);
995
+ }
996
+ throw new Error(`[LLMFactory] Unknown provider "${llmConfig.provider}"`);
997
+ }
998
+ }
999
+ static getValidator(provider) {
1000
+ const providerClass = this.getProviderClass(provider);
1001
+ return providerClass && providerClass.getValidator ? providerClass.getValidator() : null;
1002
+ }
1003
+ static getHealthChecker(provider) {
1004
+ const providerClass = this.getProviderClass(provider);
1005
+ return providerClass && providerClass.getHealthChecker ? providerClass.getHealthChecker() : null;
1006
+ }
1007
+ static getProviderClass(provider) {
1008
+ switch (provider) {
1009
+ case "openai":
1010
+ return OpenAIProvider;
1011
+ case "anthropic":
1012
+ return AnthropicProvider;
1013
+ case "ollama":
1014
+ return OllamaProvider;
1015
+ case "gemini":
1016
+ return GeminiProvider;
1017
+ case "rest":
1018
+ case "universal_rest":
1019
+ case "custom":
1020
+ return UniversalLLMAdapter;
1021
+ default:
1022
+ return null;
1023
+ }
1024
+ }
1025
+ /**
1026
+ * Creates a dedicated embedding-only provider.
1027
+ */
1028
+ static createEmbeddingProvider(embeddingConfig) {
1029
+ const fakeLLMConfig = {
1030
+ provider: embeddingConfig.provider,
1031
+ model: embeddingConfig.model,
1032
+ apiKey: embeddingConfig.apiKey,
1033
+ baseUrl: embeddingConfig.baseUrl,
1034
+ options: embeddingConfig.options
1035
+ };
1036
+ return _LLMFactory.create(fakeLLMConfig, embeddingConfig);
1037
+ }
1038
+ };
1039
+
1040
+ // src/core/ProviderRegistry.ts
1041
+ var ProviderRegistry = class {
1042
+ static registerVectorProvider(name, providerClass) {
1043
+ this.vectorProviders[name] = providerClass;
1044
+ if (providerClass.getValidator) {
1045
+ this.vectorValidators[name] = providerClass.getValidator();
1046
+ }
1047
+ if (providerClass.getHealthChecker) {
1048
+ this.vectorHealthCheckers[name] = providerClass.getHealthChecker();
1049
+ }
1050
+ }
1051
+ static async getVectorValidator(provider) {
1052
+ if (this.vectorValidators[provider]) return this.vectorValidators[provider];
1053
+ try {
1054
+ const providerClass = await this.loadVectorProviderClass(provider);
1055
+ if (providerClass.getValidator) {
1056
+ this.vectorValidators[provider] = providerClass.getValidator();
1057
+ return this.vectorValidators[provider];
1058
+ }
1059
+ } catch (e) {
1060
+ console.warn(`[ProviderRegistry] Failed to load validator for ${provider}:`, e);
1061
+ }
1062
+ return null;
1063
+ }
1064
+ static async getVectorHealthChecker(provider) {
1065
+ if (this.vectorHealthCheckers[provider]) return this.vectorHealthCheckers[provider];
1066
+ try {
1067
+ const providerClass = await this.loadVectorProviderClass(provider);
1068
+ if (providerClass.getHealthChecker) {
1069
+ this.vectorHealthCheckers[provider] = providerClass.getHealthChecker();
1070
+ return this.vectorHealthCheckers[provider];
1071
+ }
1072
+ } catch (e) {
1073
+ console.warn(`[ProviderRegistry] Failed to load health checker for ${provider}:`, e);
1074
+ }
1075
+ return null;
1076
+ }
1077
+ static async loadVectorProviderClass(provider) {
1078
+ if (this.vectorProviders[provider]) return this.vectorProviders[provider];
1079
+ switch (provider) {
1080
+ case "pinecone": {
1081
+ const { PineconeProvider } = await import("./PineconeProvider-AWFJQDZL.mjs");
1082
+ return PineconeProvider;
1083
+ }
1084
+ case "pgvector":
1085
+ case "postgresql": {
1086
+ const { PostgreSQLProvider } = await import("./PostgreSQLProvider-IEYRJ7XJ.mjs");
1087
+ return PostgreSQLProvider;
1088
+ }
1089
+ case "mongodb": {
1090
+ const { MongoDBProvider } = await import("./MongoDBProvider-COVYZDP6.mjs");
1091
+ return MongoDBProvider;
1092
+ }
1093
+ case "milvus": {
1094
+ const { MilvusProvider } = await import("./MilvusProvider-35US67MS.mjs");
1095
+ return MilvusProvider;
1096
+ }
1097
+ case "qdrant": {
1098
+ const { QdrantProvider } = await import("./QdrantProvider-M6TQYZRO.mjs");
1099
+ return QdrantProvider;
1100
+ }
1101
+ case "chromadb": {
1102
+ const { ChromaDBProvider } = await import("./ChromaDBProvider-APQVJ5F7.mjs");
1103
+ return ChromaDBProvider;
1104
+ }
1105
+ case "redis": {
1106
+ const { RedisProvider } = await import("./RedisProvider-3G5PBLZ4.mjs");
1107
+ return RedisProvider;
1108
+ }
1109
+ case "weaviate": {
1110
+ const { WeaviateProvider } = await import("./WeaviateProvider-ITHO36IL.mjs");
1111
+ return WeaviateProvider;
1112
+ }
1113
+ case "universal_rest":
1114
+ case "rest": {
1115
+ const { UniversalVectorProvider } = await import("./UniversalVectorProvider-FYQ3B2PW.mjs");
1116
+ return UniversalVectorProvider;
1117
+ }
1118
+ default:
1119
+ throw new Error(`Unsupported vector provider: ${provider}`);
1120
+ }
1121
+ }
1122
+ static async createVectorProvider(config) {
1123
+ const providerClass = await this.loadVectorProviderClass(config.provider);
1124
+ return new providerClass(config);
1125
+ }
1126
+ static async createGraphProvider(config) {
1127
+ const { provider } = config;
1128
+ if (this.graphProviders[provider]) {
1129
+ return new this.graphProviders[provider](config);
1130
+ }
1131
+ switch (provider) {
1132
+ case "simple": {
1133
+ const { SimpleGraphProvider } = await import("./SimpleGraphProvider-UK7DJW37.mjs");
1134
+ return new SimpleGraphProvider(config);
1135
+ }
1136
+ default:
1137
+ throw new Error(`Unsupported graph provider: ${provider}`);
1138
+ }
1139
+ }
1140
+ static createLLMProvider(llmConfig, embeddingConfig) {
1141
+ return LLMFactory.create(llmConfig, embeddingConfig);
1142
+ }
1143
+ };
1144
+ ProviderRegistry.vectorProviders = {};
1145
+ ProviderRegistry.graphProviders = {};
1146
+ ProviderRegistry.vectorValidators = {};
1147
+ ProviderRegistry.vectorHealthCheckers = {};
1148
+ ProviderRegistry.llmValidators = {};
1149
+ ProviderRegistry.llmHealthCheckers = {};
1150
+
1151
+ // src/core/ConfigValidator.ts
1152
+ var ConfigValidator = class {
1153
+ /**
1154
+ * Validates the entire RagConfig object.
1155
+ */
1156
+ static async validate(config) {
1157
+ const errors = [];
1158
+ if (!config.projectId) {
1159
+ errors.push({
1160
+ field: "projectId",
1161
+ message: "projectId is required",
1162
+ severity: "error"
1163
+ });
1164
+ }
1165
+ errors.push(...await this.validateVectorDbConfig(config.vectorDb));
1166
+ errors.push(...await this.validateLLMConfig(config.llm));
1167
+ if (config.embedding) {
1168
+ errors.push(...await this.validateEmbeddingConfig(config.embedding));
1169
+ } else if (config.llm.provider === "anthropic") {
1170
+ errors.push({
1171
+ field: "embedding",
1172
+ message: "Embedding config is required when using Anthropic LLM",
1173
+ suggestion: 'Provide embedding config with provider (e.g., "openai" or "ollama")',
1174
+ severity: "error"
1175
+ });
1176
+ }
1177
+ if (config.ui) {
1178
+ errors.push(...this.validateUIConfig(config.ui));
1179
+ }
1180
+ if (config.rag) {
1181
+ errors.push(...this.validateRAGConfig(config.rag));
1182
+ }
1183
+ return errors;
1184
+ }
1185
+ static async validateVectorDbConfig(config) {
1186
+ const errors = [];
1187
+ if (!config.provider) {
1188
+ errors.push({ field: "vectorDb.provider", message: "Vector database provider is required", severity: "error" });
1189
+ return errors;
1190
+ }
1191
+ if (!config.indexName) {
1192
+ errors.push({ field: "vectorDb.indexName", message: "Vector database index name is required", severity: "error" });
1193
+ }
1194
+ const validator = await ProviderRegistry.getVectorValidator(config.provider);
1195
+ if (validator) {
1196
+ errors.push(...validator.validate(config));
1197
+ } else {
1198
+ this.fallbackVectorValidation(config, errors);
1199
+ }
1200
+ return errors;
1201
+ }
1202
+ static async validateLLMConfig(config) {
1203
+ const errors = [];
1204
+ if (!config.provider) {
1205
+ errors.push({ field: "llm.provider", message: "LLM provider is required", severity: "error" });
1206
+ return errors;
1207
+ }
1208
+ const validator = LLMFactory.getValidator(config.provider);
1209
+ if (validator) {
1210
+ errors.push(...validator.validate(config));
1211
+ } else {
1212
+ this.fallbackLLMValidation(config, errors);
1213
+ }
1214
+ if (config.temperature !== void 0 && (config.temperature < 0 || config.temperature > 2)) {
1215
+ errors.push({ field: "llm.temperature", message: "Temperature must be between 0 and 2", severity: "error" });
1216
+ }
1217
+ return errors;
1218
+ }
1219
+ static async validateEmbeddingConfig(config) {
1220
+ const errors = [];
1221
+ if (!config.provider) {
1222
+ errors.push({ field: "embedding.provider", message: "Embedding provider is required", severity: "error" });
1223
+ return errors;
1224
+ }
1225
+ const validator = LLMFactory.getValidator(config.provider);
1226
+ if (validator) {
1227
+ errors.push(...validator.validate(config));
1228
+ } else {
1229
+ this.fallbackEmbeddingValidation(config, errors);
1230
+ }
1231
+ return errors;
1232
+ }
1233
+ /**
1234
+ * Temporary fallbacks for providers not yet migrated to the pluggable architecture.
1235
+ */
1236
+ static fallbackVectorValidation(config, errors) {
1237
+ const opts = config.options || {};
1238
+ switch (config.provider) {
1239
+ case "milvus":
1240
+ if (!opts.baseUrl && !opts.uri && !(opts.host && opts.port)) {
1241
+ errors.push({ field: "vectorDb.options.baseUrl", message: "Milvus connection info required", severity: "error" });
1242
+ }
1243
+ break;
1244
+ case "qdrant":
1245
+ if (!opts.baseUrl && !opts.url) {
1246
+ errors.push({ field: "vectorDb.options.baseUrl", message: "Qdrant URL required", severity: "error" });
1247
+ }
1248
+ break;
1249
+ }
1250
+ }
1251
+ static fallbackLLMValidation(config, errors) {
1252
+ if (!config.model) {
1253
+ errors.push({ field: "llm.model", message: "LLM model name is required", severity: "error" });
1254
+ }
1255
+ }
1256
+ static fallbackEmbeddingValidation(config, errors) {
1257
+ if (!config.model) {
1258
+ errors.push({ field: "embedding.model", message: "Embedding model name is required", severity: "error" });
1259
+ }
1260
+ }
1261
+ static validateUIConfig(config) {
1262
+ const errors = [];
1263
+ if (config.primaryColor && !this.isValidCSSColor(config.primaryColor)) {
1264
+ errors.push({ field: "ui.primaryColor", message: "Invalid CSS color format", severity: "warning" });
1265
+ }
1266
+ if (config.borderRadius && !UI_BORDER_RADIUS_OPTIONS.includes(config.borderRadius)) {
1267
+ errors.push({ field: "ui.borderRadius", message: `borderRadius must be one of: ${UI_BORDER_RADIUS_OPTIONS.join(", ")}`, severity: "warning" });
1268
+ }
1269
+ return errors;
1270
+ }
1271
+ static validateRAGConfig(config) {
1272
+ const errors = [];
1273
+ if (config.topK !== void 0 && (typeof config.topK !== "number" || config.topK <= 0)) {
1274
+ errors.push({ field: "rag.topK", message: "topK must be a positive integer", severity: "error" });
1275
+ }
1276
+ return errors;
1277
+ }
1278
+ static isValidCSSColor(color) {
1279
+ const hexRegex = /^#([0-9a-f]{3}){1,2}$/i;
1280
+ const rgbRegex = /^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/i;
1281
+ const namedColors = ["red", "blue", "green", "black", "white", "transparent"];
1282
+ return hexRegex.test(color) || rgbRegex.test(color) || namedColors.includes(color.toLowerCase());
1283
+ }
1284
+ static async validateAndThrow(config) {
1285
+ const errors = await this.validate(config);
1286
+ const errorItems = errors.filter((e) => e.severity === "error");
1287
+ if (errorItems.length > 0) {
1288
+ const message = errorItems.map((e) => `${e.field}: ${e.message}${e.suggestion ? ` (${e.suggestion})` : ""}`).join("\n ");
1289
+ throw new Error(`[ConfigValidator] Configuration validation failed:
1290
+ ${message}`);
1291
+ }
1292
+ }
1293
+ };
1294
+
1295
+ // src/rag/DocumentChunker.ts
1296
+ var DocumentChunker = class {
1297
+ constructor(chunkSize = 1e3, chunkOverlap = 200, separators = ["\n# ", "\n## ", "\n### ", "\n#### ", "\n\n", "\n", " ", ""]) {
1298
+ this.chunkSize = chunkSize;
1299
+ this.chunkOverlap = chunkOverlap;
1300
+ this.separators = separators;
1301
+ }
1302
+ /**
1303
+ * Split a single text string into overlapping chunks using a recursive strategy.
1304
+ * Preserves structural boundaries (Markdown headers) where possible.
1305
+ */
1306
+ chunk(text, options = {}) {
1307
+ const {
1308
+ chunkSize = this.chunkSize,
1309
+ chunkOverlap = this.chunkOverlap,
1310
+ docId = `doc_${Date.now()}`,
1311
+ metadata = {},
1312
+ separators = this.separators
1313
+ } = options;
1314
+ const finalChunks = [];
1315
+ const splits = this.recursiveSplit(text, separators, chunkSize);
1316
+ let currentChunk = [];
1317
+ let currentLength = 0;
1318
+ let chunkIndex = 0;
1319
+ for (const split of splits) {
1320
+ if (currentLength + split.length > chunkSize && currentChunk.length > 0) {
1321
+ finalChunks.push({
1322
+ id: `${docId}_chunk_${chunkIndex++}`,
1323
+ content: currentChunk.join("").trim(),
1324
+ metadata: __spreadProps(__spreadValues({}, metadata), { docId, chunkIndex: chunkIndex - 1 })
1325
+ });
1326
+ const overlapItems = [];
1327
+ let overlapLen = 0;
1328
+ for (let i = currentChunk.length - 1; i >= 0; i--) {
1329
+ if (overlapLen + currentChunk[i].length <= chunkOverlap) {
1330
+ overlapItems.unshift(currentChunk[i]);
1331
+ overlapLen += currentChunk[i].length;
1332
+ } else {
1333
+ break;
1334
+ }
1335
+ }
1336
+ currentChunk = overlapItems;
1337
+ currentLength = overlapLen;
1338
+ }
1339
+ currentChunk.push(split);
1340
+ currentLength += split.length;
1341
+ }
1342
+ if (currentChunk.length > 0) {
1343
+ finalChunks.push({
1344
+ id: `${docId}_chunk_${chunkIndex}`,
1345
+ content: currentChunk.join("").trim(),
1346
+ metadata: __spreadProps(__spreadValues({}, metadata), { docId, chunkIndex })
1338
1347
  });
1339
- } else {
1340
- payload = {
1341
- model: this.model,
1342
- input: text
1343
- };
1344
- }
1345
- const { data } = await this.http.post(path, payload);
1346
- const extractPath = (_b = this.opts.embedExtractPath) != null ? _b : "data[0].embedding";
1347
- const vector = resolvePath(data, extractPath);
1348
- if (!Array.isArray(vector)) {
1349
- throw new Error(`[UniversalLLMAdapter] Expected a number array at '${extractPath}' for embeddings.`);
1350
1348
  }
1351
- return vector;
1349
+ return finalChunks;
1352
1350
  }
1353
- async batchEmbed(texts) {
1354
- const vectors = [];
1355
- for (const text of texts) {
1356
- vectors.push(await this.embed(text));
1351
+ recursiveSplit(text, separators, chunkSize) {
1352
+ const finalSplits = [];
1353
+ let separator = separators[separators.length - 1];
1354
+ let nextSeparators = [];
1355
+ for (let i = 0; i < separators.length; i++) {
1356
+ const sep = separators[i];
1357
+ if (text.includes(sep)) {
1358
+ separator = sep;
1359
+ nextSeparators = separators.slice(i + 1);
1360
+ break;
1361
+ }
1357
1362
  }
1358
- return vectors;
1359
- }
1360
- async ping() {
1361
- try {
1362
- if (this.opts.pingPath) {
1363
- await this.http.get(this.opts.pingPath);
1363
+ const parts = text.split(separator);
1364
+ for (const part of parts) {
1365
+ if (part.length <= chunkSize) {
1366
+ finalSplits.push(part + (part === parts[parts.length - 1] ? "" : separator));
1367
+ } else if (nextSeparators.length > 0) {
1368
+ finalSplits.push(...this.recursiveSplit(part, nextSeparators, chunkSize));
1369
+ } else {
1370
+ finalSplits.push(part);
1364
1371
  }
1365
- return true;
1366
- } catch (err) {
1367
- console.error("[UniversalLLMAdapter] Ping failed:", err);
1368
- return false;
1369
1372
  }
1373
+ return finalSplits;
1374
+ }
1375
+ chunkMany(documents) {
1376
+ return documents.flatMap(
1377
+ (doc) => this.chunk(doc.content, { docId: doc.docId, metadata: doc.metadata })
1378
+ );
1370
1379
  }
1371
1380
  };
1372
1381
 
1373
- // src/llm/LLMFactory.ts
1374
- var LLMFactory = class _LLMFactory {
1375
- static create(llmConfig, embeddingConfig) {
1376
- var _a;
1377
- switch (llmConfig.provider) {
1378
- case "openai":
1379
- return new OpenAIProvider(llmConfig, embeddingConfig);
1380
- case "anthropic":
1381
- return new AnthropicProvider(llmConfig, embeddingConfig);
1382
- case "ollama":
1383
- return new OllamaProvider(llmConfig, embeddingConfig);
1384
- case "gemini":
1385
- return new GeminiProvider(llmConfig, embeddingConfig);
1386
- case "rest":
1387
- case "universal_rest":
1388
- case "custom":
1389
- return new UniversalLLMAdapter(llmConfig);
1390
- default:
1391
- if (llmConfig.baseUrl || ((_a = llmConfig.options) == null ? void 0 : _a.baseUrl)) {
1392
- return new UniversalLLMAdapter(llmConfig);
1393
- }
1394
- throw new Error(
1395
- `[LLMFactory] Unknown provider "${llmConfig.provider}". Supported: openai | anthropic | ollama | rest | custom`
1396
- );
1397
- }
1382
+ // src/rag/EntityExtractor.ts
1383
+ var EntityExtractor = class {
1384
+ constructor(llm) {
1385
+ this.llm = llm;
1398
1386
  }
1399
1387
  /**
1400
- * Creates a dedicated embedding-only provider.
1401
- * Useful when the LLM provider (e.g. Anthropic) doesn't support embeddings.
1388
+ * Extract nodes and edges from a text chunk.
1402
1389
  */
1403
- static createEmbeddingProvider(embeddingConfig) {
1404
- const fakeLLMConfig = {
1405
- provider: embeddingConfig.provider,
1406
- model: embeddingConfig.model,
1407
- apiKey: embeddingConfig.apiKey,
1408
- baseUrl: embeddingConfig.baseUrl,
1409
- options: embeddingConfig.options
1410
- };
1411
- return _LLMFactory.create(fakeLLMConfig, embeddingConfig);
1390
+ async extract(text) {
1391
+ const prompt = `
1392
+ Extract entities and relationships from the following text.
1393
+ Format the output as JSON with two keys: "nodes" (array of {id, label, properties}) and "edges" (array of {source, target, type, properties}).
1394
+ Use the same ID for the same entity.
1395
+
1396
+ Text:
1397
+ "${text}"
1398
+
1399
+ Output JSON:
1400
+ `;
1401
+ const response = await this.llm.chat([
1402
+ { role: "system", content: "You are an expert at knowledge graph extraction. Respond only with valid JSON." },
1403
+ { role: "user", content: prompt }
1404
+ ], "");
1405
+ try {
1406
+ const cleanJson = response.replace(/```json\n?|\n?```/g, "").trim();
1407
+ return JSON.parse(cleanJson);
1408
+ } catch (e) {
1409
+ console.warn("[EntityExtractor] Failed to parse LLM response as JSON:", response);
1410
+ return { nodes: [], edges: [] };
1411
+ }
1412
1412
  }
1413
1413
  };
1414
1414
 
1415
- // src/core/ProviderRegistry.ts
1416
- var ProviderRegistry = class {
1417
- static registerVectorProvider(name, providerClass) {
1418
- this.vectorProviders[name] = providerClass;
1419
- }
1415
+ // src/rag/Reranker.ts
1416
+ var Reranker = class {
1420
1417
  /**
1421
- * Register a custom graph provider class by name.
1418
+ * Re-ranks matches based on a secondary relevance score.
1419
+ * In a production environment, this would call a Cross-Encoder model.
1420
+ * Here we implement a placeholder that filters by score and limits count.
1422
1421
  */
1423
- static registerGraphProvider(name, providerClass) {
1424
- this.graphProviders[name] = providerClass;
1422
+ async rerank(matches, query, limit = 5) {
1423
+ return matches.sort((a, b) => b.score - a.score).slice(0, limit);
1425
1424
  }
1425
+ };
1426
+
1427
+ // src/rag/LlamaIndexIngestor.ts
1428
+ var LlamaIndexIngestor = class {
1426
1429
  /**
1427
- * Creates a vector database provider based on the configuration.
1428
- * Built-in providers are dynamically imported to avoid bundling all SDKs.
1430
+ * Chunks document content using LlamaIndex SentenceSplitter.
1431
+ * This respects sentence and paragraph boundaries much more effectively
1432
+ * than standard character-count splitting.
1429
1433
  */
1430
- static async createVectorProvider(config) {
1431
- const { provider } = config;
1432
- if (this.vectorProviders[provider]) {
1433
- return new this.vectorProviders[provider](config);
1434
- }
1435
- switch (provider) {
1436
- case "pinecone": {
1437
- const { PineconeProvider } = await import("./PineconeProvider-BE2JWSPD.mjs");
1438
- return new PineconeProvider(config);
1439
- }
1440
- case "pgvector":
1441
- case "postgresql": {
1442
- const { PostgreSQLProvider } = await import("./PostgreSQLProvider-5HHTK4SU.mjs");
1443
- return new PostgreSQLProvider(config);
1444
- }
1445
- case "mongodb": {
1446
- const { MongoDBProvider } = await import("./MongoDBProvider-ZKW34AEL.mjs");
1447
- return new MongoDBProvider(config);
1448
- }
1449
- case "milvus": {
1450
- const { MilvusProvider } = await import("./MilvusProvider-WDVTFB7D.mjs");
1451
- return new MilvusProvider(config);
1452
- }
1453
- case "qdrant": {
1454
- const { QdrantProvider } = await import("./QdrantProvider-XVDVBNIG.mjs");
1455
- return new QdrantProvider(config);
1456
- }
1457
- case "chromadb": {
1458
- const { ChromaDBProvider } = await import("./ChromaDBProvider-GI7TB7GJ.mjs");
1459
- return new ChromaDBProvider(config);
1460
- }
1461
- case "redis": {
1462
- const { RedisProvider } = await import("./RedisProvider-EK2R2PQH.mjs");
1463
- return new RedisProvider(config);
1464
- }
1465
- case "weaviate": {
1466
- const { WeaviateProvider } = await import("./WeaviateProvider-4CAPQ7UY.mjs");
1467
- return new WeaviateProvider(config);
1468
- }
1469
- case "universal_rest":
1470
- case "rest": {
1471
- const { UniversalVectorProvider } = await import("./UniversalVectorProvider-YIDRX6VT.mjs");
1472
- return new UniversalVectorProvider(config);
1473
- }
1474
- default:
1475
- throw new Error(
1476
- `[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).`
1477
- );
1434
+ async chunk(text, options = {}) {
1435
+ var _a, _b;
1436
+ try {
1437
+ const { Document, SentenceSplitter, MetadataMode } = await import(`${"llamaindex"}`);
1438
+ const splitter = new SentenceSplitter({
1439
+ chunkSize: (_a = options.chunkSize) != null ? _a : 1e3,
1440
+ chunkOverlap: (_b = options.chunkOverlap) != null ? _b : 200
1441
+ });
1442
+ const doc = new Document({ text, metadata: options.metadata || {} });
1443
+ const nodes = splitter.getNodesFromDocuments([doc]);
1444
+ return nodes.map((node, index) => ({
1445
+ id: `${options.docId || "doc"}_node_${index}`,
1446
+ content: node.getContent(MetadataMode.ALL),
1447
+ metadata: __spreadProps(__spreadValues(__spreadValues({}, options.metadata), node.metadata), {
1448
+ nodeId: node.id_,
1449
+ chunkIndex: index
1450
+ })
1451
+ }));
1452
+ } catch (error) {
1453
+ console.warn("[LlamaIndexIngestor] LlamaIndex package not found or failed. Falling back to default chunker.");
1454
+ throw error;
1478
1455
  }
1479
1456
  }
1480
1457
  /**
1481
- * Creates a graph database provider based on the configuration.
1458
+ * Batch processing for multiple documents.
1482
1459
  */
1483
- static async createGraphProvider(config) {
1484
- const { provider } = config;
1485
- if (this.graphProviders[provider]) {
1486
- return new this.graphProviders[provider](config);
1460
+ async chunkMany(documents, options = {}) {
1461
+ const allChunks = [];
1462
+ for (const doc of documents) {
1463
+ const chunks = await this.chunk(doc.content, __spreadProps(__spreadValues({}, options), { docId: doc.docId, metadata: doc.metadata }));
1464
+ allChunks.push(...chunks);
1487
1465
  }
1488
- switch (provider) {
1489
- case "neo4j": {
1490
- throw new Error("[ProviderRegistry] Neo4j provider not implemented yet.");
1491
- }
1492
- case "simple": {
1493
- const { SimpleGraphProvider } = await import("./SimpleGraphProvider-M6T7SE7D.mjs");
1494
- return new SimpleGraphProvider(config);
1495
- }
1496
- default:
1497
- throw new Error(
1498
- `[ProviderRegistry] Unsupported graph provider: "${provider}". Built-in providers: simple. For custom providers, call ProviderRegistry.registerGraphProvider("${provider}", YourClass).`
1499
- );
1466
+ return allChunks;
1467
+ }
1468
+ };
1469
+
1470
+ // src/core/LangChainAgent.ts
1471
+ var LangChainAgent = class {
1472
+ constructor(pipeline, config) {
1473
+ this.pipeline = pipeline;
1474
+ this.config = config;
1475
+ }
1476
+ /**
1477
+ * Initializes the agent with the RAG pipeline as a primary tool.
1478
+ * Dynamically imports LangChain dependencies to avoid build errors if missing.
1479
+ */
1480
+ async initialize(chatModel) {
1481
+ try {
1482
+ const { DynamicTool } = await import(`${"@langchain/core/tools"}`);
1483
+ const { ChatPromptTemplate, MessagesPlaceholder } = await import(`${"@langchain/core/prompts"}`);
1484
+ const { AgentExecutor, createOpenAIFunctionsAgent } = await import(`${"langchain/agents"}`);
1485
+ const searchTool = new DynamicTool({
1486
+ name: "document_search",
1487
+ description: "Use this tool to search through the knowledge base and document repository. Input should be a specific search query.",
1488
+ func: async (query) => {
1489
+ const response = await this.pipeline.ask(query);
1490
+ return `Search Results:
1491
+ ${response.reply}
1492
+
1493
+ Sources Used: ${JSON.stringify(response.sources.map((s) => s.id))}`;
1494
+ }
1495
+ });
1496
+ const tools = [searchTool];
1497
+ const prompt = ChatPromptTemplate.fromMessages([
1498
+ ["system", this.config.llm.systemPrompt || "You are a helpful AI assistant with access to a document search tool."],
1499
+ new MessagesPlaceholder("chat_history"),
1500
+ ["human", "{input}"],
1501
+ new MessagesPlaceholder("agent_scratchpad")
1502
+ ]);
1503
+ const agent = await createOpenAIFunctionsAgent({
1504
+ llm: chatModel,
1505
+ tools,
1506
+ prompt
1507
+ });
1508
+ this.executor = new AgentExecutor({
1509
+ agent,
1510
+ tools
1511
+ });
1512
+ } catch (error) {
1513
+ console.error("[LangChainAgent] Failed to initialize. Ensure 'langchain' and '@langchain/core' are installed.");
1514
+ throw error;
1500
1515
  }
1501
1516
  }
1502
1517
  /**
1503
- * Creates an LLM provider based on the configuration.
1518
+ * Run the agentic flow.
1504
1519
  */
1505
- static createLLMProvider(llmConfig, embeddingConfig) {
1506
- return LLMFactory.create(llmConfig, embeddingConfig);
1520
+ async run(input, chatHistory = []) {
1521
+ if (!this.executor) {
1522
+ throw new Error("[LangChainAgent] Agent not initialized. Call initialize() first.");
1523
+ }
1524
+ const response = await this.executor.invoke({
1525
+ input,
1526
+ chat_history: chatHistory
1527
+ });
1528
+ return response.output;
1507
1529
  }
1508
1530
  };
1509
- ProviderRegistry.vectorProviders = {};
1510
- ProviderRegistry.graphProviders = {};
1511
1531
 
1512
1532
  // src/core/BatchProcessor.ts
1513
1533
  function isTransientError(error) {
@@ -1815,122 +1835,143 @@ var EmbeddingStrategyResolver = class {
1815
1835
  }
1816
1836
  };
1817
1837
 
1818
- // src/core/Pipeline.ts
1819
- function normalizeHintValue(value) {
1820
- return value.replace(/\s+/g, " ").trim();
1821
- }
1822
- function isLikelyPromptPhrase(value) {
1823
- return /^(what|which|who|where|when|why|how)\b/i.test(value.trim());
1824
- }
1825
- function extractQueryFieldHints(question) {
1826
- var _a, _b, _c, _d;
1827
- if (!question.trim()) return [];
1828
- const hints = /* @__PURE__ */ new Map();
1829
- const addHint = (value, field) => {
1830
- const normalizedValue = normalizeHintValue(value);
1831
- if (!normalizedValue) return;
1832
- const normalizedField = field ? field.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim() : void 0;
1833
- const key = `${normalizedField != null ? normalizedField : "*"}::${normalizedValue.toLowerCase()}`;
1834
- if (!hints.has(key)) {
1835
- hints.set(key, __spreadValues({
1836
- value: normalizedValue
1837
- }, normalizedField ? { field: normalizedField } : {}));
1838
+ // src/core/QueryProcessor.ts
1839
+ var QueryProcessor = class {
1840
+ /**
1841
+ * Normalizes a string value by collapsing whitespace and trimming.
1842
+ */
1843
+ static normalizeHintValue(value) {
1844
+ return value.replace(/\s+/g, " ").trim();
1845
+ }
1846
+ /**
1847
+ * Checks if a string is likely a question word or common prompt phrase.
1848
+ */
1849
+ static isLikelyPromptPhrase(value) {
1850
+ return /^(what|which|who|where|when|why|how)\b/i.test(value.trim());
1851
+ }
1852
+ /**
1853
+ * Scans a natural language question for potential metadata hints and keywords.
1854
+ */
1855
+ static extractQueryFieldHints(question) {
1856
+ var _a, _b, _c, _d;
1857
+ if (!question.trim()) return [];
1858
+ const hints = /* @__PURE__ */ new Map();
1859
+ const addHint = (value, field) => {
1860
+ const normalizedValue = this.normalizeHintValue(value);
1861
+ if (!normalizedValue) return;
1862
+ const normalizedField = field ? field.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim() : void 0;
1863
+ const key = `${normalizedField != null ? normalizedField : "*"}::${normalizedValue.toLowerCase()}`;
1864
+ if (!hints.has(key)) {
1865
+ hints.set(key, __spreadValues({
1866
+ value: normalizedValue
1867
+ }, normalizedField ? { field: normalizedField } : {}));
1868
+ }
1869
+ };
1870
+ for (const match of question.matchAll(/["']([^"']{2,100})["']/g)) {
1871
+ addHint(match[1]);
1838
1872
  }
1839
- };
1840
- for (const match of question.matchAll(/["']([^"']{2,100})["']/g)) {
1841
- addHint(match[1]);
1842
- }
1843
- const naturalQuestionPatterns = [
1844
- /\b(?:what|which)\s+(?:is|are|was|were)\s+(?:the\s+)?([^?.!,]{1,60}?)\s+of\s+["']?([^"'\n?.!,]{2,120})["']?(?=[?.!,]|$)/gi,
1845
- /\b(?:who|what)\s+(?:is|are|was|were)\s+["']?([^"'\n?.!,]{2,120})["']?(?=[?.!,]|$)/gi,
1846
- /\b(?:about|for|regarding)\s+["']?([^"'\n?.!,]{2,120})["']?(?=[?.!,]|$)/gi
1847
- ];
1848
- const personCompanyPatterns = [
1849
- /\bcompany(?:\s+name)?\s+(?:of|for)\s+["']?([^"'\n?.!,]{2,120})["']?(?=[?.!,]|$)/gi,
1850
- /\b(?:which|what)\s+company\s+does\s+["']?([^"'\n?.!,]{2,120})["']?\s+work(?:\s+for|\s+at)?(?=[?.!,]|$)/gi,
1851
- /\bwhere\s+does\s+["']?([^"'\n?.!,]{2,120})["']?\s+work(?:\s+for|\s+at)?(?=[?.!,]|$)/gi
1852
- ];
1853
- for (const pattern of personCompanyPatterns) {
1854
- for (const match of question.matchAll(pattern)) {
1855
- const name = match[1];
1856
- if (name) addHint(name, "name");
1857
- }
1858
- }
1859
- const universalPatterns = [
1860
- { regex: /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/gi, field: "email", group: 1 },
1861
- { regex: /(\+?\d[\d\-\.\s\(\)]{6,}\d)/g, field: "phone", group: 1 },
1862
- { regex: /(\b\d{4}-\d{2}-\d{2}\b|\b\d{1,2}\/\d{1,2}\/\d{2,4}\b)/g, field: "date", group: 1 },
1863
- { regex: /(\$\s?\d{1,3}(?:,\d{3})*(?:\.\d+)?)/g, field: "amount", group: 1 },
1864
- { regex: /\b(ID|id|identifier)[: ]\s*([A-Za-z0-9\-]{3,})\b/gi, field: "id", group: 2 },
1865
- // Generic quoted phrase / proper-noun sequences as keywords (already partially handled above)
1866
- { regex: /"([^"]{2,120})"/g, group: 1 },
1867
- { regex: /'([^']{2,120})'/g, group: 1 }
1868
- ];
1869
- for (const p of universalPatterns) {
1870
- for (const match of question.matchAll(p.regex)) {
1871
- const val = p.group ? (_a = match[p.group]) != null ? _a : match[0] : match[0];
1872
- if (!val) continue;
1873
- if (p.field) addHint(val, p.field);
1874
- else addHint(val);
1875
- }
1876
- }
1877
- for (const pattern of naturalQuestionPatterns) {
1878
- for (const match of question.matchAll(pattern)) {
1879
- const value = (_b = match[2]) != null ? _b : match[1];
1880
- if (value) addHint(value);
1881
- }
1882
- }
1883
- const fieldPattern = `([^\\n:=?.!,]{1,60}?)`;
1884
- const valuePattern = `([^\\n?.!,]{1,120}?)`;
1885
- const fieldValuePatterns = [
1886
- new RegExp(`\\b${fieldPattern}\\s*(?:=|:)\\s*["']?${valuePattern}["']?(?=[?.!,]|$)`, "gi"),
1887
- new RegExp(`\\b${fieldPattern}\\s+(?:is|are|was|were|equals?|equal to|named|called)\\s+["']?${valuePattern}["']?(?=[?.!,]|$)`, "gi"),
1888
- new RegExp(`\\bwith\\s+${fieldPattern}\\s+["']?${valuePattern}["']?(?=[?.!,]|$)`, "gi")
1889
- ];
1890
- for (const pattern of fieldValuePatterns) {
1891
- for (const match of question.matchAll(pattern)) {
1892
- const field = normalizeHintValue((_c = match[1]) != null ? _c : "");
1893
- const value = (_d = match[2]) != null ? _d : "";
1894
- if (field && !isLikelyPromptPhrase(field)) {
1895
- addHint(value, field);
1896
- } else {
1897
- addHint(value);
1873
+ const naturalQuestionPatterns = [
1874
+ /\b(?:what|which)\s+(?:is|are|was|were)\s+(?:the\s+)?([^?.!,]{1,60}?)\s+of\s+["']?([^"'\n?.!,]{2,120})["']?(?=[?.!,]|$)/gi,
1875
+ /\b(?:who|what)\s+(?:is|are|was|were)\s+["']?([^"'\n?.!,]{2,120})["']?(?=[?.!,]|$)/gi,
1876
+ /\b(?:about|for|regarding)\s+["']?([^"'\n?.!,]{2,120})["']?(?=[?.!,]|$)/gi
1877
+ ];
1878
+ const personCompanyPatterns = [
1879
+ /\bcompany(?:\s+name)?\s+(?:of|for)\s+["']?([^"'\n?.!,]{2,120})["']?(?=[?.!,]|$)/gi,
1880
+ /\b(?:which|what)\s+company\s+does\s+["']?([^"'\n?.!,]{2,120})["']?\s+work(?:\s+for|\s+at)?(?=[?.!,]|$)/gi,
1881
+ /\bwhere\s+does\s+["']?([^"'\n?.!,]{2,120})["']?\s+work(?:\s+for|\s+at)?(?=[?.!,]|$)/gi
1882
+ ];
1883
+ for (const pattern of personCompanyPatterns) {
1884
+ for (const match of question.matchAll(pattern)) {
1885
+ const name = match[1];
1886
+ if (name) addHint(name, "name");
1898
1887
  }
1899
1888
  }
1889
+ const universalPatterns = [
1890
+ { regex: /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/gi, field: "email", group: 1 },
1891
+ { regex: /(\+?\d[\d\-\.\s\(\)]{6,}\d)/g, field: "phone", group: 1 },
1892
+ { regex: /(\b\d{4}-\d{2}-\d{2}\b|\b\d{1,2}\/\d{1,2}\/\d{2,4}\b)/g, field: "date", group: 1 },
1893
+ { regex: /(\$\s?\d{1,3}(?:,\d{3})*(?:\.\d+)?)/g, field: "amount", group: 1 },
1894
+ { regex: /\b(ID|id|identifier)[: ]\s*([A-Za-z0-9\-]{3,})\b/gi, field: "id", group: 2 },
1895
+ { regex: /"([^"]{2,120})"/g, group: 1 },
1896
+ { regex: /'([^']{2,120})'/g, group: 1 }
1897
+ ];
1898
+ for (const p of universalPatterns) {
1899
+ for (const match of question.matchAll(p.regex)) {
1900
+ const val = p.group ? (_a = match[p.group]) != null ? _a : match[0] : match[0];
1901
+ if (!val) continue;
1902
+ if (p.field) addHint(val, p.field);
1903
+ else addHint(val);
1904
+ }
1905
+ }
1906
+ for (const pattern of naturalQuestionPatterns) {
1907
+ for (const match of question.matchAll(pattern)) {
1908
+ const value = (_b = match[2]) != null ? _b : match[1];
1909
+ if (value) addHint(value);
1910
+ }
1911
+ }
1912
+ const fieldPattern = `([^\\n:=?.!,]{1,60}?)`;
1913
+ const valuePattern = `([^\\n?.!,]{1,120}?)`;
1914
+ const fieldValuePatterns = [
1915
+ new RegExp(`\\b${fieldPattern}\\s*(?:=|:)\\s*["']?${valuePattern}["']?(?=[?.!,]|$)`, "gi"),
1916
+ new RegExp(`\\b${fieldPattern}\\s+(?:is|are|was|were|equals?|equal to|named|called)\\s+["']?${valuePattern}["']?(?=[?.!,]|$)`, "gi"),
1917
+ new RegExp(`\\bwith\\s+${fieldPattern}\\s+["']?${valuePattern}["']?(?=[?.!,]|$)`, "gi")
1918
+ ];
1919
+ for (const pattern of fieldValuePatterns) {
1920
+ for (const match of question.matchAll(pattern)) {
1921
+ const field = this.normalizeHintValue((_c = match[1]) != null ? _c : "");
1922
+ const value = (_d = match[2]) != null ? _d : "";
1923
+ if (field && !this.isLikelyPromptPhrase(field)) {
1924
+ addHint(value, field);
1925
+ } else {
1926
+ addHint(value);
1927
+ }
1928
+ }
1929
+ }
1930
+ for (const match of question.matchAll(/\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+){0,3}\b/g)) {
1931
+ addHint(match[0]);
1932
+ }
1933
+ return [...hints.values()];
1900
1934
  }
1901
- for (const match of question.matchAll(/\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+){0,3}\b/g)) {
1902
- addHint(match[0]);
1903
- }
1904
- return [...hints.values()];
1905
- }
1906
- function buildQueryFilter(question, hints) {
1907
- const filter = { metadata: {}, keywords: [], queryText: question };
1908
- for (const hint of hints) {
1909
- if (hint.field) {
1910
- filter.metadata[hint.field] = hint.value;
1911
- } else {
1912
- filter.keywords.push(hint.value);
1935
+ /**
1936
+ * Constructs a QueryFilter object from extracted hints.
1937
+ */
1938
+ static buildQueryFilter(question, hints) {
1939
+ const filter = { metadata: {}, keywords: [], queryText: question };
1940
+ for (const hint of hints) {
1941
+ if (hint.field) {
1942
+ filter.metadata[hint.field] = hint.value;
1943
+ } else {
1944
+ filter.keywords.push(hint.value);
1945
+ }
1913
1946
  }
1947
+ for (const match of question.matchAll(/\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+){0,3}\b/g)) {
1948
+ const term = this.normalizeHintValue(match[0]);
1949
+ if (term && !filter.keywords.includes(term)) filter.keywords.push(term);
1950
+ }
1951
+ if (Object.keys(filter.metadata || {}).length === 0) delete filter.metadata;
1952
+ if (filter.keywords && filter.keywords.length === 0) delete filter.keywords;
1953
+ return filter;
1914
1954
  }
1915
- for (const match of question.matchAll(/\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+){0,3}\b/g)) {
1916
- const term = normalizeHintValue(match[0]);
1917
- if (term && !filter.keywords.includes(term)) filter.keywords.push(term);
1918
- }
1919
- if (Object.keys(filter.metadata || {}).length === 0) delete filter.metadata;
1920
- if (filter.keywords && filter.keywords.length === 0) delete filter.keywords;
1921
- return filter;
1922
- }
1955
+ };
1956
+
1957
+ // src/core/Pipeline.ts
1923
1958
  var Pipeline = class {
1924
1959
  constructor(config) {
1925
- this.initialised = false;
1926
- var _a, _b, _c, _d;
1927
1960
  this.config = config;
1961
+ this.embeddingCache = /* @__PURE__ */ new Map();
1962
+ this.initialised = false;
1963
+ var _a, _b, _c, _d, _e;
1928
1964
  this.chunker = new DocumentChunker(
1929
1965
  (_b = (_a = config.rag) == null ? void 0 : _a.chunkSize) != null ? _b : 1e3,
1930
1966
  (_d = (_c = config.rag) == null ? void 0 : _c.chunkOverlap) != null ? _d : 200
1931
1967
  );
1968
+ if (((_e = config.rag) == null ? void 0 : _e.chunkingStrategy) === "llamaindex") {
1969
+ this.llamaIngestor = new LlamaIndexIngestor();
1970
+ }
1971
+ this.reranker = new Reranker();
1932
1972
  }
1933
1973
  async initialize() {
1974
+ var _a;
1934
1975
  if (this.initialised) return;
1935
1976
  this.vectorDB = await ProviderRegistry.createVectorProvider(this.config.vectorDb);
1936
1977
  const { llmProvider, embeddingProvider } = await EmbeddingStrategyResolver.resolve(
@@ -1945,6 +1986,10 @@ var Pipeline = class {
1945
1986
  this.entityExtractor = new EntityExtractor(this.llmProvider);
1946
1987
  }
1947
1988
  await this.vectorDB.initialize();
1989
+ if (((_a = this.config.rag) == null ? void 0 : _a.architecture) === "agentic") {
1990
+ this.agent = new LangChainAgent(this, this.config);
1991
+ await this.agent.initialize(this.llmProvider);
1992
+ }
1948
1993
  this.initialised = true;
1949
1994
  }
1950
1995
  /**
@@ -1957,53 +2002,21 @@ var Pipeline = class {
1957
2002
  const results = [];
1958
2003
  for (const doc of documents) {
1959
2004
  try {
1960
- const chunks = this.chunker.chunk(doc.content, {
1961
- docId: doc.docId,
1962
- metadata: doc.metadata
1963
- });
1964
- const embedBatchOptions = {
1965
- batchSize: 50,
1966
- maxRetries: 3,
1967
- initialDelayMs: 100
1968
- };
1969
- const vectors = await BatchProcessor.mapWithRetry(
1970
- chunks.map((c) => c.content),
1971
- (text) => this.embeddingProvider.embed(text, { taskType: "document" }),
1972
- embedBatchOptions
1973
- );
1974
- if (vectors.length !== chunks.length) {
1975
- throw new Error(`Embedding mismatch: got ${vectors.length} vectors for ${chunks.length} chunks`);
1976
- }
2005
+ const chunks = await this.prepareChunks(doc);
2006
+ const vectors = await this.processEmbeddings(chunks);
1977
2007
  const upsertDocs = chunks.map((chunk, i) => ({
1978
2008
  id: chunk.id,
1979
2009
  vector: vectors[i],
1980
2010
  content: chunk.content,
1981
2011
  metadata: chunk.metadata
1982
2012
  }));
1983
- const upsertBatchOptions = {
1984
- batchSize: 100,
1985
- maxRetries: 3,
1986
- initialDelayMs: 100
1987
- };
1988
- const upsertResult = await BatchProcessor.processBatch(
1989
- upsertDocs,
1990
- (batch) => this.vectorDB.batchUpsert(batch, ns),
1991
- upsertBatchOptions
1992
- );
1993
- if (upsertResult.errors.length > 0) {
1994
- console.warn(`[Pipeline] Ingestion: ${upsertResult.errors.length} batches failed for doc ${doc.docId}`);
1995
- }
2013
+ const totalProcessed = await this.processUpserts(upsertDocs, ns);
1996
2014
  results.push({
1997
2015
  docId: doc.docId,
1998
- chunksIngested: upsertResult.totalProcessed
2016
+ chunksIngested: totalProcessed
1999
2017
  });
2000
2018
  if (this.graphDB && this.entityExtractor) {
2001
- console.log(`[Pipeline] Extracting entities for doc ${doc.docId}...`);
2002
- for (const chunk of chunks) {
2003
- const { nodes, edges } = await this.entityExtractor.extract(chunk.content);
2004
- if (nodes.length > 0) await this.graphDB.addNodes(nodes);
2005
- if (edges.length > 0) await this.graphDB.addEdges(edges);
2006
- }
2019
+ await this.processGraphIngestion(doc.docId, chunks);
2007
2020
  }
2008
2021
  } catch (error) {
2009
2022
  console.error(`[Pipeline] Failed to ingest document ${doc.docId}:`, error);
@@ -2012,45 +2025,210 @@ var Pipeline = class {
2012
2025
  }
2013
2026
  return results;
2014
2027
  }
2028
+ /**
2029
+ * Step 1: Chunk the document content.
2030
+ */
2031
+ async prepareChunks(doc) {
2032
+ var _a, _b;
2033
+ if (this.llamaIngestor) {
2034
+ return await this.llamaIngestor.chunk(doc.content, {
2035
+ docId: doc.docId,
2036
+ metadata: doc.metadata,
2037
+ chunkSize: (_a = this.config.rag) == null ? void 0 : _a.chunkSize,
2038
+ chunkOverlap: (_b = this.config.rag) == null ? void 0 : _b.chunkOverlap
2039
+ });
2040
+ }
2041
+ return this.chunker.chunk(doc.content, {
2042
+ docId: doc.docId,
2043
+ metadata: doc.metadata
2044
+ });
2045
+ }
2046
+ /**
2047
+ * Step 2: Generate embeddings for chunks with retry logic.
2048
+ */
2049
+ async processEmbeddings(chunks) {
2050
+ const embedBatchOptions = {
2051
+ batchSize: 50,
2052
+ maxRetries: 3,
2053
+ initialDelayMs: 100
2054
+ };
2055
+ const vectors = await BatchProcessor.mapWithRetry(
2056
+ chunks.map((c) => c.content),
2057
+ (text) => this.embeddingProvider.embed(text, { taskType: "document" }),
2058
+ embedBatchOptions
2059
+ );
2060
+ if (vectors.length !== chunks.length) {
2061
+ throw new Error(`Embedding mismatch: got ${vectors.length} vectors for ${chunks.length} chunks`);
2062
+ }
2063
+ return vectors;
2064
+ }
2065
+ /**
2066
+ * Step 3: Upsert chunks to vector database with retry logic.
2067
+ */
2068
+ async processUpserts(upsertDocs, namespace) {
2069
+ const upsertBatchOptions = {
2070
+ batchSize: 100,
2071
+ maxRetries: 3,
2072
+ initialDelayMs: 100
2073
+ };
2074
+ const upsertResult = await BatchProcessor.processBatch(
2075
+ upsertDocs,
2076
+ (batch) => this.vectorDB.batchUpsert(batch, namespace),
2077
+ upsertBatchOptions
2078
+ );
2079
+ if (upsertResult.errors.length > 0) {
2080
+ console.warn(`[Pipeline] Ingestion: ${upsertResult.errors.length} batches failed`);
2081
+ }
2082
+ return upsertResult.totalProcessed;
2083
+ }
2084
+ /**
2085
+ * Step 4: Optional graph-based entity extraction and ingestion.
2086
+ */
2087
+ async processGraphIngestion(docId, chunks) {
2088
+ console.log(`[Pipeline] Extracting entities for doc ${docId} (${chunks.length} chunks)...`);
2089
+ const extractionOptions = {
2090
+ batchSize: 2,
2091
+ // Low concurrency for LLM extraction
2092
+ maxRetries: 1,
2093
+ initialDelayMs: 500
2094
+ };
2095
+ await BatchProcessor.processBatch(
2096
+ chunks,
2097
+ async (batch) => {
2098
+ for (const chunk of batch) {
2099
+ try {
2100
+ const { nodes, edges } = await this.entityExtractor.extract(chunk.content);
2101
+ if (nodes.length > 0) await this.graphDB.addNodes(nodes);
2102
+ if (edges.length > 0) await this.graphDB.addEdges(edges);
2103
+ } catch (err) {
2104
+ console.warn(`[Pipeline] Entity extraction failed for chunk:`, err);
2105
+ }
2106
+ }
2107
+ },
2108
+ extractionOptions
2109
+ );
2110
+ }
2015
2111
  async ask(question, history = [], namespace) {
2016
- var _a, _b, _c, _d, _e, _f;
2112
+ var _a;
2017
2113
  await this.initialize();
2018
- const ns = namespace != null ? namespace : this.config.projectId;
2019
- const topK = (_b = (_a = this.config.rag) == null ? void 0 : _a.topK) != null ? _b : 5;
2020
- const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0;
2114
+ if (((_a = this.config.rag) == null ? void 0 : _a.architecture) === "agentic" && this.agent) {
2115
+ console.log("[Pipeline] \u{1F916} Executing in Agentic Mode...");
2116
+ const agentReply = await this.agent.run(question, history);
2117
+ return { reply: agentReply, sources: [] };
2118
+ }
2119
+ const stream = this.askStream(question, history, namespace);
2120
+ let reply = "";
2121
+ let sources = [];
2122
+ let graphData;
2021
2123
  try {
2022
- let searchQuery = question;
2023
- if ((_e = this.config.rag) == null ? void 0 : _e.useQueryTransformation) {
2024
- searchQuery = await this.rewriteQuery(question, history);
2124
+ for (var iter = __forAwait(stream), more, temp, error; more = !(temp = await iter.next()).done; more = false) {
2125
+ const chunk = temp.value;
2126
+ if (typeof chunk === "string") {
2127
+ reply += chunk;
2128
+ } else if ("sources" in chunk) {
2129
+ sources = chunk.sources;
2130
+ graphData = chunk.graphData;
2131
+ }
2025
2132
  }
2026
- const queryVector = await this.embeddingProvider.embed(searchQuery, { taskType: "query" });
2027
- const fieldHints = extractQueryFieldHints(question);
2028
- const filter = buildQueryFilter(question, fieldHints);
2029
- filter.__entityHints = fieldHints;
2030
- const rawMatches = await this.vectorDB.query(queryVector, topK, ns, filter);
2031
- const sources = rawMatches.filter((m) => m.score >= scoreThreshold);
2032
- let graphData;
2033
- if (this.graphDB && ((_f = this.config.rag) == null ? void 0 : _f.useGraphRetrieval)) {
2034
- graphData = await this.graphDB.query(searchQuery);
2133
+ } catch (temp) {
2134
+ error = [temp];
2135
+ } finally {
2136
+ try {
2137
+ more && (temp = iter.return) && await temp.call(iter);
2138
+ } finally {
2139
+ if (error)
2140
+ throw error[0];
2035
2141
  }
2036
- let context = sources.length ? sources.map((m, i) => `[Source ${i + 1}]
2142
+ }
2143
+ return { reply, sources, graphData };
2144
+ }
2145
+ /**
2146
+ * High-performance streaming RAG flow.
2147
+ * Yields text chunks first, then the retrieval metadata at the end.
2148
+ */
2149
+ askStream(_0) {
2150
+ return __asyncGenerator(this, arguments, function* (question, history = [], namespace) {
2151
+ var _a, _b, _c, _d, _e, _f;
2152
+ yield new __await(this.initialize());
2153
+ const ns = namespace != null ? namespace : this.config.projectId;
2154
+ const topK = (_b = (_a = this.config.rag) == null ? void 0 : _a.topK) != null ? _b : 5;
2155
+ const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0;
2156
+ try {
2157
+ let searchQuery = question;
2158
+ if ((_e = this.config.rag) == null ? void 0 : _e.useQueryTransformation) {
2159
+ searchQuery = yield new __await(this.rewriteQuery(question, history));
2160
+ }
2161
+ const hints = QueryProcessor.extractQueryFieldHints(question);
2162
+ const filter = QueryProcessor.buildQueryFilter(question, hints);
2163
+ const { sources: rawSources, graphData } = yield new __await(this.retrieve(searchQuery, {
2164
+ namespace: ns,
2165
+ topK: topK * 2,
2166
+ filter
2167
+ }));
2168
+ let sources = rawSources.filter((m) => m.score >= scoreThreshold);
2169
+ if ((_f = this.config.rag) == null ? void 0 : _f.useReranking) {
2170
+ sources = yield new __await(this.reranker.rerank(sources, question, topK));
2171
+ } else {
2172
+ sources = sources.slice(0, topK);
2173
+ }
2174
+ let context = sources.length ? sources.map((m, i) => `[Source ${i + 1}]
2037
2175
  ${m.content}`).join("\n\n---\n\n") : "No relevant context found.";
2038
- if (graphData && graphData.nodes.length > 0) {
2039
- const graphContext = graphData.nodes.map(
2040
- (n) => `Entity: ${n.label} (${n.id})${n.properties ? " - " + JSON.stringify(n.properties) : ""}`
2041
- ).join("\n");
2042
- context = `GRAPH KNOWLEDGE:
2176
+ if (graphData && graphData.nodes.length > 0) {
2177
+ const graphContext = graphData.nodes.map(
2178
+ (n) => `Entity: ${n.label} (${n.id})${n.properties ? " - " + JSON.stringify(n.properties) : ""}`
2179
+ ).join("\n");
2180
+ context = `GRAPH KNOWLEDGE:
2043
2181
  ${graphContext}
2044
2182
 
2045
2183
  VECTOR CONTEXT:
2046
2184
  ${context}`;
2185
+ }
2186
+ const messages = [...history, { role: "user", content: question }];
2187
+ if (this.llmProvider.chatStream) {
2188
+ try {
2189
+ for (var iter = __forAwait(this.llmProvider.chatStream(messages, context)), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
2190
+ const chunk = temp.value;
2191
+ yield chunk;
2192
+ }
2193
+ } catch (temp) {
2194
+ error = [temp];
2195
+ } finally {
2196
+ try {
2197
+ more && (temp = iter.return) && (yield new __await(temp.call(iter)));
2198
+ } finally {
2199
+ if (error)
2200
+ throw error[0];
2201
+ }
2202
+ }
2203
+ } else {
2204
+ const reply = yield new __await(this.llmProvider.chat(messages, context));
2205
+ yield reply;
2206
+ }
2207
+ yield { reply: "", sources, graphData };
2208
+ } catch (error2) {
2209
+ throw new Error(`[Pipeline] Stream failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
2047
2210
  }
2048
- const messages = [...history, { role: "user", content: question }];
2049
- const reply = await this.llmProvider.chat(messages, context);
2050
- return { reply, sources, graphData };
2051
- } catch (error) {
2052
- throw new Error(`[Pipeline] Chat failed: ${error instanceof Error ? error.message : String(error)}`);
2211
+ });
2212
+ }
2213
+ /**
2214
+ * Universal retrieval method combining all enabled providers.
2215
+ */
2216
+ async retrieve(query, options) {
2217
+ var _a, _b, _c;
2218
+ const ns = (_a = options.namespace) != null ? _a : this.config.projectId;
2219
+ const topK = (_b = options.topK) != null ? _b : 5;
2220
+ const cacheKey = `${ns}::${query}`;
2221
+ let queryVector = this.embeddingCache.get(cacheKey);
2222
+ const [retrievedVector, graphData] = await Promise.all([
2223
+ queryVector ? Promise.resolve(queryVector) : this.embeddingProvider.embed(query, { taskType: "query" }),
2224
+ this.graphDB && ((_c = this.config.rag) == null ? void 0 : _c.useGraphRetrieval) ? this.graphDB.query(query) : Promise.resolve(void 0)
2225
+ ]);
2226
+ if (!queryVector) {
2227
+ this.embeddingCache.set(cacheKey, retrievedVector);
2228
+ queryVector = retrievedVector;
2053
2229
  }
2230
+ const sources = await this.vectorDB.query(queryVector, topK, ns, options.filter);
2231
+ return { sources, graphData };
2054
2232
  }
2055
2233
  /**
2056
2234
  * Rewrite the user query for better retrieval performance.
@@ -2081,11 +2259,11 @@ var ProviderHealthCheck = class {
2081
2259
  */
2082
2260
  static async checkVectorProvider(config) {
2083
2261
  const timestamp = Date.now();
2084
- const configErrors = ConfigValidator.validate({
2262
+ const configErrors = await ConfigValidator.validate({
2085
2263
  projectId: "health-check",
2086
2264
  vectorDb: config,
2087
- llm: { provider: "openai", model: "gpt-4o" },
2088
- embedding: { provider: "openai", model: "text-embedding-3-small" }
2265
+ llm: { provider: "openai", model: "gpt-4o", apiKey: "none" },
2266
+ embedding: { provider: "openai", model: "text-embedding-3-small", apiKey: "none" }
2089
2267
  });
2090
2268
  const vectorDbErrors = configErrors.filter((e) => e.field.startsWith("vectorDb"));
2091
2269
  if (vectorDbErrors.length > 0) {
@@ -2097,35 +2275,11 @@ var ProviderHealthCheck = class {
2097
2275
  };
2098
2276
  }
2099
2277
  try {
2100
- switch (config.provider) {
2101
- case "pinecone":
2102
- return await this.checkPinecone(config, timestamp);
2103
- case "pgvector":
2104
- case "postgresql":
2105
- return await this.checkPostgres(config, timestamp);
2106
- case "mongodb":
2107
- return await this.checkMongoDB(config, timestamp);
2108
- case "milvus":
2109
- return await this.checkMilvus(config, timestamp);
2110
- case "qdrant":
2111
- return await this.checkQdrant(config, timestamp);
2112
- case "chromadb":
2113
- return await this.checkChromaDB(config, timestamp);
2114
- case "redis":
2115
- return await this.checkRedis(config, timestamp);
2116
- case "weaviate":
2117
- return await this.checkWeaviate(config, timestamp);
2118
- case "rest":
2119
- case "universal_rest":
2120
- return await this.checkRestAPI(config, timestamp);
2121
- default:
2122
- return {
2123
- healthy: false,
2124
- provider: config.provider,
2125
- error: `Unsupported provider: ${config.provider}`,
2126
- timestamp
2127
- };
2278
+ const checker = await ProviderRegistry.getVectorHealthChecker(config.provider);
2279
+ if (checker) {
2280
+ return await checker.check(config);
2128
2281
  }
2282
+ return await this.fallbackVectorHealthCheck(config, timestamp);
2129
2283
  } catch (error) {
2130
2284
  return {
2131
2285
  healthy: false,
@@ -2135,363 +2289,50 @@ var ProviderHealthCheck = class {
2135
2289
  };
2136
2290
  }
2137
2291
  }
2138
- static async checkPinecone(config, timestamp) {
2139
- var _a, _b;
2140
- const opts = config.options;
2141
- try {
2142
- const { Pinecone } = await import("@pinecone-database/pinecone");
2143
- const client = new Pinecone({ apiKey: opts.apiKey });
2144
- const indexes = await client.listIndexes();
2145
- const indexNames = (_b = (_a = indexes.indexes) == null ? void 0 : _a.map((i) => i.name)) != null ? _b : [];
2146
- if (!indexNames.includes(config.indexName)) {
2292
+ static async fallbackVectorHealthCheck(config, timestamp) {
2293
+ const opts = config.options || {};
2294
+ const baseUrl = opts.baseUrl || opts.uri;
2295
+ if (baseUrl && baseUrl.startsWith("http")) {
2296
+ try {
2297
+ const response = await fetch(`${baseUrl.replace(/\/$/, "")}/health`);
2147
2298
  return {
2148
- healthy: false,
2149
- provider: "pinecone",
2150
- error: `Index "${config.indexName}" not found. Available: ${indexNames.join(", ")}`,
2299
+ healthy: response.ok,
2300
+ provider: config.provider,
2301
+ error: response.ok ? void 0 : `Health check failed: ${response.status}`,
2151
2302
  timestamp
2152
2303
  };
2153
- }
2154
- return {
2155
- healthy: true,
2156
- provider: "pinecone",
2157
- capabilities: { indexes: indexNames.length, targetIndex: config.indexName },
2158
- timestamp
2159
- };
2160
- } catch (error) {
2161
- return {
2162
- healthy: false,
2163
- provider: "pinecone",
2164
- error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
2165
- timestamp
2166
- };
2167
- }
2168
- }
2169
- static async checkPostgres(config, timestamp) {
2170
- const opts = config.options;
2171
- try {
2172
- const { Client } = await import("pg");
2173
- const client = new Client({ connectionString: opts.connectionString });
2174
- await client.connect();
2175
- const result = await client.query(`
2176
- SELECT EXISTS(SELECT 1 FROM pg_extension WHERE extname = 'vector');
2177
- `);
2178
- const hasVector = result.rows[0].exists;
2179
- await client.end();
2180
- return {
2181
- healthy: true,
2182
- provider: "postgresql",
2183
- capabilities: { pgvectorInstalled: hasVector },
2184
- timestamp
2185
- };
2186
- } catch (error) {
2187
- return {
2188
- healthy: false,
2189
- provider: "postgresql",
2190
- error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
2191
- timestamp
2192
- };
2193
- }
2194
- }
2195
- static async checkMongoDB(config, timestamp) {
2196
- const opts = config.options;
2197
- try {
2198
- const { MongoClient } = await import("mongodb");
2199
- const client = new MongoClient(opts.uri);
2200
- await client.connect();
2201
- const db = client.db(opts.database);
2202
- const collections = await db.listCollections().toArray();
2203
- const collectionNames = collections.map((c) => c.name);
2204
- const hasCollection = collectionNames.includes(opts.collection);
2205
- await client.close();
2206
- return {
2207
- healthy: true,
2208
- provider: "mongodb",
2209
- capabilities: {
2210
- collections: collectionNames.length,
2211
- targetCollection: hasCollection ? opts.collection : "NOT FOUND (will be created on first insert)"
2212
- },
2213
- timestamp
2214
- };
2215
- } catch (error) {
2216
- return {
2217
- healthy: false,
2218
- provider: "mongodb",
2219
- error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
2220
- timestamp
2221
- };
2222
- }
2223
- }
2224
- /**
2225
- * Milvus health check — uses baseUrl/uri (matching MilvusProvider constructor).
2226
- */
2227
- static async checkMilvus(config, timestamp) {
2228
- const opts = config.options;
2229
- const baseUrl = opts.baseUrl || opts.uri || (opts.host ? `http://${opts.host}:${opts.port || 19530}` : "http://localhost:19530");
2230
- try {
2231
- const controller = new AbortController();
2232
- const timeoutId = setTimeout(() => controller.abort(), 5e3);
2233
- const response = await fetch(`${baseUrl}/v1/health`, { signal: controller.signal });
2234
- clearTimeout(timeoutId);
2235
- if (!response.ok) {
2236
- throw new Error(`Health check returned ${response.status}`);
2237
- }
2238
- return {
2239
- healthy: true,
2240
- provider: "milvus",
2241
- capabilities: { endpoint: baseUrl },
2242
- timestamp
2243
- };
2244
- } catch (error) {
2245
- return {
2246
- healthy: false,
2247
- provider: "milvus",
2248
- error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
2249
- timestamp
2250
- };
2251
- }
2252
- }
2253
- /**
2254
- * Qdrant health check — uses baseUrl (matching QdrantProvider constructor).
2255
- */
2256
- static async checkQdrant(config, timestamp) {
2257
- const opts = config.options;
2258
- const baseUrl = (opts.baseUrl || opts.url || "http://localhost:6333").replace(/\/$/, "");
2259
- try {
2260
- const apiKey = opts.apiKey;
2261
- const response = await fetch(`${baseUrl}/`, {
2262
- headers: apiKey ? { "api-key": apiKey } : {}
2263
- });
2264
- if (!response.ok) throw new Error(`Health check returned ${response.status}`);
2265
- const health = await response.json();
2266
- return {
2267
- healthy: true,
2268
- provider: "qdrant",
2269
- capabilities: health,
2270
- timestamp
2271
- };
2272
- } catch (error) {
2273
- return {
2274
- healthy: false,
2275
- provider: "qdrant",
2276
- error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
2277
- timestamp
2278
- };
2279
- }
2280
- }
2281
- /**
2282
- * ChromaDB health check — uses baseUrl/host (matching ChromaDBProvider constructor).
2283
- */
2284
- static async checkChromaDB(config, timestamp) {
2285
- const opts = config.options;
2286
- const baseUrl = opts.baseUrl || (opts.host ? `http://${opts.host}:${opts.port || 8e3}` : "http://localhost:8000");
2287
- try {
2288
- const response = await fetch(`${baseUrl}/api/v1/heartbeat`);
2289
- if (!response.ok) throw new Error(`Health check returned ${response.status}`);
2290
- return {
2291
- healthy: true,
2292
- provider: "chromadb",
2293
- capabilities: { endpoint: baseUrl },
2294
- timestamp
2295
- };
2296
- } catch (error) {
2297
- return {
2298
- healthy: false,
2299
- provider: "chromadb",
2300
- error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
2301
- timestamp
2302
- };
2303
- }
2304
- }
2305
- /**
2306
- * Redis health check — Redis is TCP-only (no HTTP endpoint).
2307
- * We report healthy=true with a note; actual connectivity is validated at first operation.
2308
- */
2309
- static async checkRedis(config, timestamp) {
2310
- const opts = config.options;
2311
- const url = opts.baseUrl || opts.url || (opts.host && opts.port ? `redis://${opts.host}:${opts.port}` : "redis://localhost:6379");
2312
- if (url.startsWith("http")) {
2313
- try {
2314
- await fetch(url);
2315
- return { healthy: true, provider: "redis", capabilities: { endpoint: url }, timestamp };
2316
2304
  } catch (e) {
2305
+ return { healthy: false, provider: config.provider, error: "Provider unreachable", timestamp };
2317
2306
  }
2318
2307
  }
2319
2308
  return {
2320
2309
  healthy: true,
2321
- provider: "redis",
2322
- capabilities: {
2323
- endpoint: url,
2324
- note: "Redis uses TCP; connectivity is validated on first operation."
2325
- },
2310
+ provider: config.provider,
2311
+ error: "Pluggable health check not implemented; basic reachability assumed.",
2326
2312
  timestamp
2327
2313
  };
2328
2314
  }
2329
- /**
2330
- * Weaviate health check — uses baseUrl/url (matching WeaviateProvider constructor).
2331
- */
2332
- static async checkWeaviate(config, timestamp) {
2333
- const opts = config.options;
2334
- const baseUrl = (opts.baseUrl || opts.url || "http://localhost:8080").replace(/\/$/, "");
2335
- try {
2336
- const response = await fetch(`${baseUrl}/v1/.well-known/ready`);
2337
- if (!response.ok) throw new Error(`Health check returned ${response.status}`);
2338
- return {
2339
- healthy: true,
2340
- provider: "weaviate",
2341
- capabilities: { endpoint: baseUrl },
2342
- timestamp
2343
- };
2344
- } catch (error) {
2345
- return {
2346
- healthy: false,
2347
- provider: "weaviate",
2348
- error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
2349
- timestamp
2350
- };
2351
- }
2352
- }
2353
- static async checkRestAPI(config, timestamp) {
2354
- const opts = config.options;
2355
- const baseUrl = (opts.baseUrl || "").replace(/\/$/, "");
2356
- if (!baseUrl) {
2357
- return { healthy: false, provider: "rest", error: "baseUrl is required", timestamp };
2358
- }
2359
- try {
2360
- const response = await fetch(`${baseUrl}/health`, {
2361
- headers: opts.headers ? JSON.parse(opts.headers) : {}
2362
- });
2363
- return {
2364
- healthy: response.ok,
2365
- provider: "rest",
2366
- error: response.ok ? void 0 : `Health check returned ${response.status}`,
2367
- timestamp
2368
- };
2369
- } catch (error) {
2370
- return {
2371
- healthy: false,
2372
- provider: "rest",
2373
- error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
2374
- timestamp
2375
- };
2376
- }
2377
- }
2378
2315
  /**
2379
2316
  * Validates LLM provider configuration.
2380
2317
  */
2381
2318
  static async checkLLMProvider(config) {
2382
2319
  const timestamp = Date.now();
2383
2320
  try {
2384
- switch (config.provider) {
2385
- case "openai":
2386
- return await this.checkOpenAI(config, timestamp);
2387
- case "anthropic":
2388
- return await this.checkAnthropic(config, timestamp);
2389
- case "ollama":
2390
- return await this.checkOllama(config, timestamp);
2391
- case "rest":
2392
- case "universal_rest":
2393
- return await this.checkLLMRestAPI(config, timestamp);
2394
- default:
2395
- return {
2396
- healthy: false,
2397
- provider: config.provider,
2398
- error: `Unsupported provider: ${config.provider}`,
2399
- timestamp
2400
- };
2321
+ const checker = LLMFactory.getHealthChecker(config.provider);
2322
+ if (checker) {
2323
+ return await checker.check(config);
2401
2324
  }
2402
- } catch (error) {
2403
- return {
2404
- healthy: false,
2405
- provider: config.provider,
2406
- error: error instanceof Error ? error.message : String(error),
2407
- timestamp
2408
- };
2409
- }
2410
- }
2411
- static async checkOpenAI(config, timestamp) {
2412
- try {
2413
- const OpenAI2 = await import("openai");
2414
- const client = new OpenAI2.default({ apiKey: config.apiKey });
2415
- const models = await client.models.list();
2416
- const hasModel = models.data.some((m) => m.id === config.model);
2417
- return {
2418
- healthy: true,
2419
- provider: "openai",
2420
- capabilities: { model: config.model, available: hasModel, totalModels: models.data.length },
2421
- timestamp
2422
- };
2423
- } catch (error) {
2424
- return {
2425
- healthy: false,
2426
- provider: "openai",
2427
- error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
2428
- timestamp
2429
- };
2430
- }
2431
- }
2432
- static async checkAnthropic(config, timestamp) {
2433
- try {
2434
- const { default: Anthropic2 } = await import("@anthropic-ai/sdk");
2435
- const client = new Anthropic2({ apiKey: config.apiKey });
2436
- await client.messages.create({
2437
- model: config.model,
2438
- max_tokens: 10,
2439
- messages: [{ role: "user", content: "ping" }]
2440
- });
2441
- return { healthy: true, provider: "anthropic", capabilities: { model: config.model }, timestamp };
2442
- } catch (error) {
2443
- return {
2444
- healthy: false,
2445
- provider: "anthropic",
2446
- error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
2447
- timestamp
2448
- };
2449
- }
2450
- }
2451
- static async checkOllama(config, timestamp) {
2452
- const baseUrl = (config.baseUrl || "http://localhost:11434").replace(/\/$/, "");
2453
- try {
2454
- const response = await fetch(`${baseUrl}/api/tags`);
2455
- if (!response.ok) throw new Error(`Health check returned ${response.status}`);
2456
- const data = await response.json();
2457
- const models = data.models || [];
2458
- const hasModel = models.some((m) => m.name === config.model);
2459
2325
  return {
2460
2326
  healthy: true,
2461
- provider: "ollama",
2462
- capabilities: { model: config.model, available: hasModel, totalModels: models.length },
2463
- timestamp
2464
- };
2465
- } catch (error) {
2466
- return {
2467
- healthy: false,
2468
- provider: "ollama",
2469
- error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
2470
- timestamp
2471
- };
2472
- }
2473
- }
2474
- static async checkLLMRestAPI(config, timestamp) {
2475
- var _a, _b;
2476
- const baseUrlRaw = config.baseUrl || ((_a = config.options) == null ? void 0 : _a.baseUrl);
2477
- const baseUrl = (typeof baseUrlRaw === "string" ? baseUrlRaw : "").replace(/\/$/, "");
2478
- if (!baseUrl) {
2479
- return { healthy: false, provider: config.provider, error: "baseUrl is required", timestamp };
2480
- }
2481
- try {
2482
- const headers = (_b = config.options) == null ? void 0 : _b.headers;
2483
- const response = await fetch(`${baseUrl}/health`, { headers: headers || void 0 });
2484
- return {
2485
- healthy: response.ok,
2486
2327
  provider: config.provider,
2487
- error: response.ok ? void 0 : `Health check returned ${response.status}`,
2328
+ error: "Pluggable health check not implemented; basic reachability assumed.",
2488
2329
  timestamp
2489
2330
  };
2490
2331
  } catch (error) {
2491
2332
  return {
2492
2333
  healthy: false,
2493
2334
  provider: config.provider,
2494
- error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
2335
+ error: error instanceof Error ? error.message : String(error),
2495
2336
  timestamp
2496
2337
  };
2497
2338
  }
@@ -2599,7 +2440,7 @@ var DocumentParser = class {
2599
2440
  }
2600
2441
  if (extension === "pdf" || mimeType === "application/pdf") {
2601
2442
  try {
2602
- const pdf = await import("pdf-parse/lib/pdf-parse.js");
2443
+ const pdf = await import("pdf-parse");
2603
2444
  const buffer = Buffer.isBuffer(file) ? file : Buffer.from(await file.arrayBuffer());
2604
2445
  const data = await pdf.default(buffer);
2605
2446
  return data.text;
@@ -2722,8 +2563,6 @@ function createUploadHandler(config) {
2722
2563
  export {
2723
2564
  getRagConfig,
2724
2565
  ConfigResolver,
2725
- ConfigValidator,
2726
- DocumentChunker,
2727
2566
  OpenAIProvider,
2728
2567
  AnthropicProvider,
2729
2568
  OllamaProvider,
@@ -2732,6 +2571,8 @@ export {
2732
2571
  UniversalLLMAdapter,
2733
2572
  LLMFactory,
2734
2573
  ProviderRegistry,
2574
+ ConfigValidator,
2575
+ DocumentChunker,
2735
2576
  BatchProcessor,
2736
2577
  EmbeddingStrategy,
2737
2578
  EmbeddingStrategyResolver,