@devilsdev/rag-pipeline-utils 2.4.5 → 2.6.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 (258) hide show
  1. package/.ragrc.schema.json +83 -192
  2. package/CHANGELOG.md +431 -0
  3. package/README.md +418 -117
  4. package/SECURITY.md +5 -5
  5. package/dist/experimental.cjs +2 -0
  6. package/dist/experimental.d.ts +1 -0
  7. package/dist/experimental.mjs +5 -0
  8. package/dist/index.cjs +6442 -1355
  9. package/dist/index.d.ts +129 -2
  10. package/dist/index.mjs +1 -1
  11. package/dist/testing.cjs +2 -0
  12. package/dist/testing.d.ts +1 -0
  13. package/dist/testing.mjs +5 -0
  14. package/dist/types/adapters/ai-sdk.d.ts +53 -0
  15. package/dist/types/agentic/agentic-pipeline.d.ts +103 -0
  16. package/dist/types/agentic/iterative-retriever.d.ts +85 -0
  17. package/dist/types/agentic/query-planner.d.ts +145 -0
  18. package/dist/types/agentic/self-critique.d.ts +45 -0
  19. package/dist/types/ai/adaptive-retrieval.d.ts +44 -0
  20. package/dist/types/ai/federation/federated-learning-coordinator.d.ts +62 -0
  21. package/dist/types/ai/multimodal-processing.d.ts +32 -0
  22. package/dist/types/ai/multimodal.d.ts +39 -0
  23. package/dist/types/ai/retrieval-engine.d.ts +37 -0
  24. package/dist/types/ai/training/model-training-orchestrator.d.ts +31 -0
  25. package/dist/types/cache/component-keys.d.ts +7 -0
  26. package/dist/types/cache/memory-cache.d.ts +54 -0
  27. package/dist/types/cache/pipeline-cache.d.ts +66 -0
  28. package/dist/types/cache/redis-cache.d.ts +46 -0
  29. package/dist/types/chunking/chunking-engine.d.ts +70 -0
  30. package/dist/types/chunking/strategies/fixed-size.d.ts +17 -0
  31. package/dist/types/chunking/strategies/recursive.d.ts +20 -0
  32. package/dist/types/chunking/strategies/semantic.d.ts +19 -0
  33. package/dist/types/chunking/strategies/sentence.d.ts +13 -0
  34. package/dist/types/chunking/strategies/structure-aware.d.ts +19 -0
  35. package/dist/types/citation/citation-tracker.d.ts +64 -0
  36. package/dist/types/citation/hallucination-detector.d.ts +40 -0
  37. package/dist/types/citation/source-mapper.d.ts +37 -0
  38. package/dist/types/connectors/anthropic-connector.d.ts +97 -0
  39. package/dist/types/connectors/base-connector.d.ts +87 -0
  40. package/dist/types/connectors/cohere-connector.d.ts +94 -0
  41. package/dist/types/connectors/connector-utils.d.ts +110 -0
  42. package/dist/types/connectors/local-embedder.d.ts +95 -0
  43. package/dist/types/connectors/memory-retriever.d.ts +99 -0
  44. package/dist/types/connectors/ollama-connector.d.ts +115 -0
  45. package/dist/types/connectors/openai-connector.d.ts +129 -0
  46. package/dist/types/connectors/rag-prompt.d.ts +40 -0
  47. package/dist/types/core/create-pipeline.d.ts +8 -0
  48. package/dist/types/core/plugin-registry.d.ts +136 -0
  49. package/dist/types/core/stage-contracts.d.ts +36 -0
  50. package/dist/types/core/validate-plugin.d.ts +45 -0
  51. package/dist/types/cost/cost-calculator.d.ts +152 -0
  52. package/dist/types/cost/cost-tracker.d.ts +126 -0
  53. package/dist/types/cost/token-budget.d.ts +110 -0
  54. package/dist/types/dag/core/error-context.d.ts +239 -0
  55. package/dist/types/dag/core/execution-scheduler.d.ts +191 -0
  56. package/dist/types/dag/core/topology-validation.d.ts +226 -0
  57. package/dist/types/dag/dag-engine.d.ts +156 -0
  58. package/dist/types/dag/dag-node.d.ts +37 -0
  59. package/dist/types/dashboard/dashboard-generator.d.ts +127 -0
  60. package/dist/types/dashboard/metrics-aggregator.d.ts +218 -0
  61. package/dist/types/debug/execution-tracer.d.ts +57 -0
  62. package/dist/types/debug/trace-inspector.d.ts +31 -0
  63. package/dist/types/ecosystem/plugin-certification.d.ts +169 -0
  64. package/dist/types/enterprise/data-governance.d.ts +203 -0
  65. package/dist/types/enterprise/multi-tenancy.d.ts +137 -0
  66. package/dist/types/enterprise/sso-integration.d.ts +420 -0
  67. package/dist/types/evaluate/context-metrics.d.ts +55 -0
  68. package/dist/types/evaluate/faithfulness.d.ts +41 -0
  69. package/dist/types/evaluate/groundedness.d.ts +60 -0
  70. package/dist/types/evaluate/pipeline-evaluator.d.ts +88 -0
  71. package/dist/types/evaluate/relevance.d.ts +21 -0
  72. package/dist/types/evaluate/scoring.d.ts +123 -0
  73. package/dist/types/evaluate/semantic-similarity.d.ts +14 -0
  74. package/dist/types/experimental.d.ts +15 -0
  75. package/dist/types/graph/entity-extractor.d.ts +84 -0
  76. package/dist/types/graph/graph-index.d.ts +49 -0
  77. package/dist/types/graph/graph-retriever.d.ts +81 -0
  78. package/dist/types/graph/knowledge-graph.d.ts +129 -0
  79. package/dist/types/grounding/check-answer.d.ts +101 -0
  80. package/dist/types/guardrails/guardrails-pipeline.d.ts +63 -0
  81. package/dist/types/guardrails/injection-detector.d.ts +47 -0
  82. package/dist/types/guardrails/post-generation-guard.d.ts +61 -0
  83. package/dist/types/guardrails/pre-retrieval-guard.d.ts +64 -0
  84. package/dist/types/guardrails/retrieval-guard.d.ts +57 -0
  85. package/dist/types/mcp/mcp-server.d.ts +85 -0
  86. package/dist/types/mcp/mcp-tool-builder.d.ts +55 -0
  87. package/dist/types/reranker/cascade-reranker.d.ts +40 -0
  88. package/dist/types/reranker/embedding-reranker.d.ts +77 -0
  89. package/dist/types/reranker/scoring-reranker.d.ts +41 -0
  90. package/dist/types/retrieval/bm25-search.d.ts +43 -0
  91. package/dist/types/retrieval/file-vector-store.d.ts +99 -0
  92. package/dist/types/retrieval/hybrid-retriever.d.ts +35 -0
  93. package/dist/types/retrieval/rank-fusion.d.ts +10 -0
  94. package/dist/types/security/jwt-config.d.ts +80 -0
  95. package/dist/types/security/jwt-validator.d.ts +195 -0
  96. package/dist/types/security/plugin-sandbox.d.ts +158 -0
  97. package/dist/types/security/plugin-signature-verifier.d.ts +139 -0
  98. package/dist/types/security/validator.d.ts +5 -0
  99. package/dist/types/streaming/embedding-stream.d.ts +65 -0
  100. package/dist/types/streaming/sse-adapter.d.ts +59 -0
  101. package/dist/types/streaming/stream-router.d.ts +88 -0
  102. package/dist/types/streaming/streaming-embedder.d.ts +99 -0
  103. package/dist/types/streaming/websocket-adapter.d.ts +71 -0
  104. package/dist/types/templates/scaffold.d.ts +55 -0
  105. package/dist/types/templates/template-registry.d.ts +63 -0
  106. package/dist/types/testing.d.ts +168 -0
  107. package/dist/types/utils/embed-query.d.ts +20 -0
  108. package/dist/types/utils/logger.d.ts +50 -0
  109. package/dist/types/utils/rate-limiter.d.ts +66 -0
  110. package/dist/types/utils/retry.d.ts +27 -0
  111. package/dist/types/utils/run-abort.d.ts +34 -0
  112. package/dist/types/utils/secure-logging.d.ts +51 -0
  113. package/dist/types/utils/structured-logger.d.ts +126 -0
  114. package/package.json +72 -33
  115. package/src/adapters/ai-sdk.js +215 -0
  116. package/src/agentic/agentic-pipeline.js +75 -7
  117. package/src/agentic/iterative-retriever.js +47 -10
  118. package/src/agentic/query-planner.js +329 -78
  119. package/src/cache/component-keys.js +38 -0
  120. package/src/cache/memory-cache.js +87 -0
  121. package/src/cache/pipeline-cache.js +260 -0
  122. package/src/cache/redis-cache.js +83 -0
  123. package/src/citation/citation-tracker.js +6 -4
  124. package/src/citation/hallucination-detector.js +2 -2
  125. package/src/citation/source-mapper.js +15 -28
  126. package/src/cli/commands/ai.js +106 -0
  127. package/src/cli/commands/plugin-hub/event-handlers.js +6 -0
  128. package/src/cli/commands/plugin-hub/hub-commands.js +0 -74
  129. package/src/cli/commands/plugin-hub/local-commands.js +143 -0
  130. package/src/cli/commands/plugin-hub.js +114 -49
  131. package/src/cli/diagnostics/config-checker.js +76 -63
  132. package/src/cli/diagnostics/environment-checker.js +62 -31
  133. package/src/cli/diagnostics/plugin-checker.js +36 -89
  134. package/src/cli/doctor-command.js +39 -5
  135. package/src/cli/enhanced-cli-commands.js +104 -233
  136. package/src/cli/handlers/ai-handler.js +364 -0
  137. package/src/cli/handlers/diagnostics-handler.js +78 -31
  138. package/src/cli/handlers/ingest-handler.js +28 -22
  139. package/src/cli/handlers/init-handler.js +148 -25
  140. package/src/cli/handlers/query-handler.js +203 -25
  141. package/src/cli/handlers/run-handler.js +16 -167
  142. package/src/cli/handlers/serve-handler.js +60 -0
  143. package/src/cli/services/bench-stats.js +54 -0
  144. package/src/cli/services/benchmark.js +161 -0
  145. package/src/cli/services/feedback.js +155 -0
  146. package/src/cli/services/instrument.js +83 -0
  147. package/src/cli/services/rag-service.js +361 -0
  148. package/src/cli/shared/cli-utils.js +24 -7
  149. package/src/cli/shared/config-context.js +21 -0
  150. package/src/cli/wizard/config-builder.js +81 -0
  151. package/src/cli/wizard/init-wizard.js +602 -0
  152. package/src/config/component-factory.js +125 -0
  153. package/src/config/enhanced-ragrc-schema.js +0 -2
  154. package/src/config/load-config.js +29 -10
  155. package/src/config/load-plugin-config.js +46 -2
  156. package/src/config/validate-schema.js +4 -1
  157. package/src/connectors/anthropic-connector.js +115 -57
  158. package/src/connectors/base-connector.js +74 -19
  159. package/src/connectors/cohere-connector.js +108 -34
  160. package/src/connectors/connector-utils.js +302 -0
  161. package/src/connectors/local-embedder.js +63 -5
  162. package/src/connectors/memory-retriever.js +125 -14
  163. package/src/connectors/ollama-connector.js +112 -51
  164. package/src/connectors/openai-connector.js +153 -44
  165. package/src/connectors/rag-prompt.js +88 -0
  166. package/src/core/create-pipeline.js +307 -61
  167. package/src/core/performance/parallel-processor.js +62 -1
  168. package/src/core/plugin-registry.js +125 -26
  169. package/src/core/stage-contracts.js +36 -0
  170. package/src/core/validate-plugin.js +85 -0
  171. package/src/cost/cost-calculator.js +205 -32
  172. package/src/debug/execution-tracer.js +14 -4
  173. package/src/dev/dev-server.js +173 -11
  174. package/src/dev/hot-reload.js +44 -4
  175. package/src/ecosystem/plugin-checks.js +181 -0
  176. package/src/ecosystem/plugin-hub.js +50 -125
  177. package/src/ecosystem/plugin-installer.js +298 -0
  178. package/src/ecosystem/plugin-probe.js +127 -0
  179. package/src/ecosystem/plugin-scanner.js +185 -0
  180. package/src/ecosystem/tar-extract.js +114 -0
  181. package/src/enterprise/audit-logging.js +158 -29
  182. package/src/enterprise/sso-integration.js +716 -228
  183. package/src/evaluate/context-metrics.js +28 -16
  184. package/src/evaluate/evaluator.js +3 -3
  185. package/src/evaluate/faithfulness.js +18 -14
  186. package/src/evaluate/groundedness.js +43 -12
  187. package/src/evaluate/pipeline-evaluator.js +35 -8
  188. package/src/evaluate/scoring.js +251 -3
  189. package/src/evaluate/semantic-similarity.js +79 -0
  190. package/src/experimental.js +71 -0
  191. package/src/grounding/check-answer.js +173 -0
  192. package/src/guardrails/guardrails-pipeline.js +69 -7
  193. package/src/guardrails/injection-detector.js +283 -0
  194. package/src/guardrails/post-generation-guard.js +19 -0
  195. package/src/guardrails/pre-retrieval-guard.js +36 -36
  196. package/src/guardrails/retrieval-guard.js +58 -8
  197. package/src/index.js +58 -1
  198. package/src/loader/csv-loader.js +3 -2
  199. package/src/loader/document-reader.js +91 -0
  200. package/src/loader/html-loader.js +3 -1
  201. package/src/loader/markdown-loader.js +3 -2
  202. package/src/reranker/cascade-reranker.js +3 -0
  203. package/src/reranker/embedding-reranker.js +136 -21
  204. package/src/reranker/llm-reranker.js +79 -12
  205. package/src/reranker/scoring-reranker.js +3 -0
  206. package/src/retrieval/bm25-search.js +12 -6
  207. package/src/retrieval/file-vector-store.js +241 -0
  208. package/src/retrieval/hybrid-retriever.js +22 -19
  209. package/src/security/jwt-config.js +13 -2
  210. package/src/security/jwt-validator.js +139 -34
  211. package/src/security/plugin-sandbox.js +18 -1
  212. package/src/security/plugin-signature-verifier.js +245 -70
  213. package/src/testing.js +254 -0
  214. package/src/utils/deprecate.js +69 -0
  215. package/src/utils/embed-query.js +33 -0
  216. package/src/utils/input-sanitizer.js +19 -4
  217. package/src/utils/logger.js +34 -4
  218. package/src/utils/run-abort.js +102 -0
  219. package/src/ai/federated-learning.js +0 -211
  220. package/src/ai/federation.js +0 -71
  221. package/src/ai/index.js +0 -22
  222. package/src/ai/model-trainer.js +0 -217
  223. package/src/ai/model-training.js +0 -202
  224. package/src/ai/multimodal/multi-modal-processor.js +0 -345
  225. package/src/ai/orchestrator.js +0 -65
  226. package/src/ai/retrieval/adaptive-retrieval-engine.js +0 -398
  227. package/src/cli/commands/ai-ml/adaptive-commands.js +0 -178
  228. package/src/cli/commands/ai-ml/federated-commands.js +0 -207
  229. package/src/cli/commands/ai-ml/index.js +0 -17
  230. package/src/cli/commands/ai-ml/multimodal-commands.js +0 -187
  231. package/src/cli/commands/ai-ml/shared.js +0 -51
  232. package/src/cli/commands/ai-ml/training-commands.js +0 -228
  233. package/src/cli/commands/ai-ml.js +0 -386
  234. package/src/cli/commands/docs.js +0 -228
  235. package/src/cli/commands/dx.js +0 -538
  236. package/src/cli/commands/wizard.js +0 -627
  237. package/src/cli/input-sanitization-middleware.js +0 -356
  238. package/src/cli/interactive-wizard.js +0 -373
  239. package/src/cli/templates/development.template.json +0 -64
  240. package/src/cli/templates/minimal.template.json +0 -33
  241. package/src/cli/templates/production.template.json +0 -104
  242. package/src/cli/templates/testing.template.json +0 -45
  243. package/src/cli/wizard/configuration.js +0 -148
  244. package/src/cli/wizard/observability.js +0 -79
  245. package/src/cli/wizard/performance.js +0 -76
  246. package/src/cli/wizard/plugin-selection.js +0 -297
  247. package/src/cli/wizard/preview-save.js +0 -172
  248. package/src/cli/wizard/project-setup.js +0 -66
  249. package/src/core/pipeline-factory.js +0 -59
  250. package/src/dag/enhanced-dag-engine.js +0 -894
  251. package/src/dx/index.js +0 -40
  252. package/src/dx/integration-templates.js +0 -662
  253. package/src/dx/performance-profiler.js +0 -968
  254. package/src/dx/realtime-debugger.js +0 -755
  255. package/src/dx/visual-pipeline-builder.js +0 -578
  256. package/src/ingest.js +0 -59
  257. package/src/pipeline/pipeline.js +0 -18
  258. package/src/query.js +0 -117
@@ -0,0 +1,69 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Deprecation wrappers for public exports (see SEMVER.md, "Deprecation
5
+ * process"): the export keeps working and warns once per process when used.
6
+ */
7
+
8
+ const wrapped = new WeakMap();
9
+ const warned = new Set();
10
+
11
+ function warnOnce(code, message) {
12
+ if (warned.has(code)) return;
13
+ warned.add(code);
14
+ process.emitWarning(message, { type: 'DeprecationWarning', code });
15
+ }
16
+
17
+ /**
18
+ * Wrap a class so constructing it (or calling a static method) emits a
19
+ * one-time DeprecationWarning. A Proxy keeps the original's identity for
20
+ * prototype, name, statics, `extends` and `instanceof`, so instances created
21
+ * inside the library still satisfy `instanceof Wrapped`. The same class
22
+ * always gets the same wrapper, so aliases stay identical.
23
+ *
24
+ * @param {Function} Class - Class to wrap
25
+ * @param {object} info
26
+ * @param {string} info.name - Export name, used in the message and code
27
+ * @param {string} info.message - Full warning text
28
+ * @returns {Function} The wrapped class
29
+ */
30
+ function deprecateClass(Class, { name, message }) {
31
+ if (wrapped.has(Class)) return wrapped.get(Class);
32
+ // SSOManager -> RAG_DEP_SSO_MANAGER, OAuth2Provider -> RAG_DEP_O_AUTH2_PROVIDER
33
+ const snake = name
34
+ .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
35
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
36
+ .toUpperCase();
37
+ const code = `RAG_DEP_${snake}`;
38
+ const proxy = new Proxy(Class, {
39
+ construct(target, args, newTarget) {
40
+ warnOnce(code, message);
41
+ return Reflect.construct(
42
+ target,
43
+ args,
44
+ newTarget === proxy ? target : newTarget,
45
+ );
46
+ },
47
+ get(target, property, receiver) {
48
+ const value = Reflect.get(target, property, receiver);
49
+ // Only the class's own static methods; inherited Function.prototype
50
+ // members (call, bind, toString, Symbol.hasInstance for instanceof)
51
+ // must not warn.
52
+ const ownStatic =
53
+ typeof value === 'function' &&
54
+ property !== 'prototype' &&
55
+ Object.prototype.hasOwnProperty.call(target, property);
56
+ if (ownStatic) {
57
+ return function deprecatedStatic(...args) {
58
+ warnOnce(code, message);
59
+ return value.apply(this === proxy ? target : this, args);
60
+ };
61
+ }
62
+ return value;
63
+ },
64
+ });
65
+ wrapped.set(Class, proxy);
66
+ return proxy;
67
+ }
68
+
69
+ module.exports = { deprecateClass };
@@ -0,0 +1,33 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Embed a single query string with any supported embedder shape.
5
+ *
6
+ * Prefers embedQuery(); falls back to embed(). Accepts the common return
7
+ * shapes: a vector, a list of vectors, or [{ values | vector | embedding }].
8
+ *
9
+ * @param {object} embedder - Object with embedQuery() or embed()
10
+ * @param {string} query - Text to embed
11
+ * @param {{ signal?: AbortSignal }} [options] - Passed to the embedder as its
12
+ * second argument (only when given, so embedders keep their usual arity)
13
+ * @returns {Promise<number[]|undefined>} The query vector
14
+ */
15
+ async function embedQuery(embedder, query, options) {
16
+ const args = options ? [query, options] : [query];
17
+ const raw = embedder.embedQuery
18
+ ? await embedder.embedQuery(...args)
19
+ : await embedder.embed(...args);
20
+ const first = Array.isArray(raw) && typeof raw[0] !== 'number' ? raw[0] : raw;
21
+ if (Array.isArray(first)) return first;
22
+ return first?.values || first?.vector || first?.embedding;
23
+ }
24
+
25
+ /**
26
+ * @param {object} [embedder]
27
+ * @returns {boolean} Whether embedQuery() can use this embedder
28
+ */
29
+ function canEmbed(embedder) {
30
+ return Boolean(embedder && (embedder.embedQuery || embedder.embed));
31
+ }
32
+
33
+ module.exports = { embedQuery, canEmbed };
@@ -65,6 +65,21 @@ const ValidationPatterns = {
65
65
  hex: /^[0-9a-fA-F]+$/,
66
66
  };
67
67
 
68
+ /**
69
+ * Test a shared pattern from start of string. The DangerousPatterns regexes
70
+ * carry the /g flag (needed by replace()), and RegExp#test() on a /g regex
71
+ * resumes from lastIndex left by the previous call, which made a match
72
+ * against one input skip part of the next and let payloads through.
73
+ *
74
+ * @param {RegExp} pattern
75
+ * @param {string} str
76
+ * @returns {boolean}
77
+ */
78
+ function matchesPattern(pattern, str) {
79
+ pattern.lastIndex = 0;
80
+ return pattern.test(str);
81
+ }
82
+
68
83
  /**
69
84
  * Dangerous patterns that indicate attacks (compiled for performance)
70
85
  */
@@ -476,7 +491,7 @@ class InputSanitizer {
476
491
 
477
492
  // Apply all XSS patterns
478
493
  for (const pattern of DangerousPatterns.xss) {
479
- if (pattern.test(sanitized)) {
494
+ if (matchesPattern(pattern, sanitized)) {
480
495
  this.stats.blocked++;
481
496
  sanitized = sanitized.replace(pattern, '');
482
497
  }
@@ -497,7 +512,7 @@ class InputSanitizer {
497
512
 
498
513
  // Check for SQL injection patterns
499
514
  for (const pattern of DangerousPatterns.sql) {
500
- if (pattern.test(sanitized)) {
515
+ if (matchesPattern(pattern, sanitized)) {
501
516
  this.stats.blocked++;
502
517
  if (this.options.throwOnInvalid) {
503
518
  throw new Error('Potential SQL injection detected');
@@ -520,7 +535,7 @@ class InputSanitizer {
520
535
  _sanitizeCommand(str) {
521
536
  // Check for command injection patterns
522
537
  for (const pattern of DangerousPatterns.command) {
523
- if (pattern.test(str)) {
538
+ if (matchesPattern(pattern, str)) {
524
539
  this.stats.blocked++;
525
540
  if (this.options.throwOnInvalid) {
526
541
  throw new Error('Potential command injection detected');
@@ -560,7 +575,7 @@ class InputSanitizer {
560
575
 
561
576
  // Check for path traversal patterns in both original and fully decoded versions
562
577
  for (const pattern of DangerousPatterns.pathTraversal) {
563
- if (pattern.test(str) || pattern.test(current)) {
578
+ if (matchesPattern(pattern, str) || matchesPattern(pattern, current)) {
564
579
  this.stats.blocked++;
565
580
  // SECURITY: Always throw on path traversal - it should never be silently sanitized
566
581
  // Empty paths could resolve to application root, creating security vulnerabilities
@@ -31,9 +31,20 @@ const getLogLevel = () => {
31
31
  // Determine if pretty printing should be enabled
32
32
  const shouldUsePrettyPrint = () => {
33
33
  // Enable pretty print in development or when explicitly requested
34
- return (
35
- process.env.NODE_ENV === 'development' || process.env.LOG_PRETTY === 'true'
36
- );
34
+ const requested =
35
+ process.env.NODE_ENV === 'development' || process.env.LOG_PRETTY === 'true';
36
+ return requested && isPinoPrettyAvailable();
37
+ };
38
+
39
+ // pino-pretty is an optional peer dependency; pino throws at startup if the
40
+ // transport target cannot be resolved, so fall back to JSON output instead.
41
+ const isPinoPrettyAvailable = () => {
42
+ try {
43
+ require.resolve('pino-pretty');
44
+ return true;
45
+ } catch {
46
+ return false;
47
+ }
37
48
  };
38
49
 
39
50
  /**
@@ -68,6 +79,25 @@ const baseLogger = pino({
68
79
  }),
69
80
  });
70
81
 
82
+ /**
83
+ * Coerce the meta argument into an object. Callers often pass
84
+ * console-style extras such as `logger.error("Failed:", error.message)`;
85
+ * spreading a string would log one key per character.
86
+ *
87
+ * @param {*} meta
88
+ * @returns {object}
89
+ */
90
+ function normalizeMeta(meta) {
91
+ if (meta == null) return {};
92
+ if (meta instanceof Error) {
93
+ return { error: meta.message, stack: meta.stack };
94
+ }
95
+ if (typeof meta !== 'object' || Array.isArray(meta)) {
96
+ return { detail: meta };
97
+ }
98
+ return meta;
99
+ }
100
+
71
101
  /**
72
102
  * Enhanced logger with correlation ID support and structured context
73
103
  */
@@ -118,7 +148,7 @@ class StructuredLogger {
118
148
  ...this.context,
119
149
  correlationId: this.correlationId || crypto.randomUUID(),
120
150
  timestamp: new Date().toISOString(),
121
- ...meta,
151
+ ...normalizeMeta(meta),
122
152
  };
123
153
 
124
154
  this.logger[level](logData, message);
@@ -0,0 +1,102 @@
1
+ 'use strict';
2
+
3
+ /** Message used for a run that exceeded its timeout. */
4
+ const TIMEOUT_MESSAGE = 'Execution timeout';
5
+ /** Message used for a run cancelled through the caller's AbortSignal. */
6
+ const ABORTED_MESSAGE = 'Execution aborted';
7
+
8
+ function timeoutError() {
9
+ const err = new Error(TIMEOUT_MESSAGE);
10
+ err.name = 'TimeoutError';
11
+ err.code = 'ETIMEDOUT';
12
+ return err;
13
+ }
14
+
15
+ function callerAbortError(signal) {
16
+ const err = new Error(ABORTED_MESSAGE);
17
+ err.name = 'AbortError';
18
+ err.code = 'ABORT_ERR';
19
+ if (signal && signal.reason !== undefined) err.cause = signal.reason;
20
+ return err;
21
+ }
22
+
23
+ /**
24
+ * Per-run cancellation: one AbortController that is aborted when `timeout`
25
+ * elapses or when the caller's `signal` aborts. Its `signal` is handed to
26
+ * every component call; `race()` makes the run settle as soon as it is
27
+ * aborted even if a component ignores the signal; `throwIfAborted()` stops
28
+ * work between stages.
29
+ *
30
+ * @param {object} [options]
31
+ * @param {number} [options.timeout] - Milliseconds before the run is aborted
32
+ * @param {AbortSignal} [options.signal] - Caller's signal to follow
33
+ * @returns {{
34
+ * signal: AbortSignal,
35
+ * race: (promise: Promise<any>) => Promise<any>,
36
+ * throwIfAborted: () => void,
37
+ * clearTimer: () => void,
38
+ * detach: () => void,
39
+ * cleanup: () => void
40
+ * }}
41
+ */
42
+ function createRunAbort({ timeout, signal } = {}) {
43
+ const controller = new AbortController();
44
+ let timer = null;
45
+ let onCallerAbort = null;
46
+ let rejectAborted;
47
+ const aborted = new Promise((_, reject) => {
48
+ rejectAborted = reject;
49
+ });
50
+ // The rejection is observed through race(); never report it as unhandled.
51
+ aborted.catch(() => {});
52
+
53
+ const abort = (error) => {
54
+ if (controller.signal.aborted) return;
55
+ controller.abort(error);
56
+ rejectAborted(error);
57
+ };
58
+
59
+ if (typeof timeout === 'number' && timeout > 0) {
60
+ timer = setTimeout(() => abort(timeoutError()), timeout);
61
+ }
62
+ if (signal && typeof signal.addEventListener === 'function') {
63
+ if (signal.aborted) {
64
+ abort(callerAbortError(signal));
65
+ } else {
66
+ onCallerAbort = () => abort(callerAbortError(signal));
67
+ signal.addEventListener('abort', onCallerAbort, { once: true });
68
+ }
69
+ }
70
+
71
+ const clearTimer = () => {
72
+ if (timer) clearTimeout(timer);
73
+ timer = null;
74
+ };
75
+ const detach = () => {
76
+ if (onCallerAbort) signal.removeEventListener('abort', onCallerAbort);
77
+ onCallerAbort = null;
78
+ };
79
+
80
+ return {
81
+ signal: controller.signal,
82
+ race(promise) {
83
+ if (controller.signal.aborted) {
84
+ // Swallow the late outcome of work that is no longer awaited.
85
+ Promise.resolve(promise).catch(() => {});
86
+ return Promise.reject(controller.signal.reason);
87
+ }
88
+ return Promise.race([promise, aborted]);
89
+ },
90
+ throwIfAborted() {
91
+ if (controller.signal.aborted) throw controller.signal.reason;
92
+ },
93
+ clearTimer,
94
+ detach,
95
+ cleanup() {
96
+ clearTimer();
97
+ detach();
98
+ },
99
+ };
100
+ }
101
+
102
+ module.exports = { createRunAbort, TIMEOUT_MESSAGE, ABORTED_MESSAGE };
@@ -1,211 +0,0 @@
1
- /**
2
- * Federated Learning Coordinator
3
- * Event-driven federated learning with deterministic aggregation
4
- */
5
-
6
- "use strict";
7
-
8
- const { EventEmitter } = require("events");
9
-
10
- // In-memory storage for federations
11
- const federations = new Map();
12
- let federationCounter = 0;
13
-
14
- // Create EventEmitter instance
15
- const eventEmitter = new EventEmitter();
16
-
17
- /**
18
- * Create a new federation
19
- * @param {string} tenantId - Tenant identifier
20
- * @param {object} modelConfig - Model configuration
21
- * @param {object} options - Federation options
22
- * @returns {string} Federation ID
23
- */
24
- function createFederation(tenantId, modelConfig, options = {}) {
25
- const federationId = `fed-${++federationCounter}`;
26
- const federation = {
27
- tenantId,
28
- modelConfig,
29
- options,
30
- rounds: 0,
31
- participants: [],
32
- };
33
-
34
- federations.set(federationId, federation);
35
- return federationId;
36
- }
37
-
38
- /**
39
- * Register multiple participants in a federation
40
- * @param {string} federationId - Federation ID
41
- * @param {array} participantsArray - Participants to register
42
- * @returns {Promise<string|array>} Single string ID if one participant, array of IDs if multiple
43
- */
44
- async function registerParticipants(federationId, participantsArray) {
45
- const federation = federations.get(federationId);
46
- if (!federation) {
47
- throw new Error(`Federation ${federationId} not found`);
48
- }
49
-
50
- const participantIds = [];
51
- for (const participant of participantsArray) {
52
- // Validate participant - throw error if invalid
53
- if (!participant || typeof participant !== "object") {
54
- throw new Error("Participant not eligible");
55
- }
56
-
57
- // Check data size requirement
58
- if (
59
- typeof participant.dataSize === "number" &&
60
- participant.dataSize < 100
61
- ) {
62
- throw new Error("Participant not eligible");
63
- }
64
-
65
- const participantId =
66
- participant.id || `p-${federation.participants.length + 1}`;
67
- federation.participants.push({
68
- id: participantId,
69
- status: "active",
70
- weight: participant.weight || 1,
71
- ...participant,
72
- });
73
- participantIds.push(participantId);
74
- }
75
-
76
- // Return single string if only one participant, array if multiple (for test compatibility)
77
- return participantsArray.length === 1 ? participantIds[0] : participantIds;
78
- }
79
-
80
- /**
81
- * Conduct a federated learning round
82
- * @param {string} federationId - Federation ID
83
- * @returns {object} Round result
84
- */
85
- function conductRound(federationId) {
86
- const federation = federations.get(federationId);
87
- if (!federation) {
88
- throw new Error(`Federation ${federationId} not found`);
89
- }
90
-
91
- const round = ++federation.rounds;
92
- const roundId = `r-${Date.now()}`;
93
-
94
- // Emit round started event
95
- eventEmitter.emit("federated_round_started", {
96
- federationId,
97
- round,
98
- });
99
-
100
- // Compute deterministic convergence metrics
101
- const participants = federation.participants;
102
- const globalAccuracy = 0.9 + participants.length * 0.01; // Slight improvement with more participants
103
- const loss = Math.max(0.1 - participants.length * 0.005, 0.01); // Lower loss with more participants
104
-
105
- // Emit round completed event
106
- eventEmitter.emit("federated_round_completed", {
107
- federationId,
108
- metrics: { globalAccuracy, loss },
109
- });
110
-
111
- return {
112
- roundId,
113
- round,
114
- participants: participants.length,
115
- convergence: {
116
- globalAccuracy: Math.round(globalAccuracy * 1000) / 1000,
117
- loss: Math.round(loss * 1000) / 1000,
118
- },
119
- };
120
- }
121
-
122
- /**
123
- * Get federation statistics
124
- * @param {string} federationId - Federation ID
125
- * @returns {object} Federation stats
126
- */
127
- function getStats(federationId) {
128
- const federation = federations.get(federationId);
129
- if (!federation) {
130
- throw new Error(`Federation ${federationId} not found`);
131
- }
132
-
133
- return {
134
- federation: {
135
- id: federationId,
136
- rounds: federation.rounds,
137
- },
138
- participants: federation.participants.map((p) => ({
139
- id: p.id,
140
- status: p.status || "active",
141
- weight: p.weight || 1,
142
- })),
143
- performance: {
144
- averageAccuracy: 0.92,
145
- convergenceRate: 0.85,
146
- communicationRounds: federation.rounds,
147
- },
148
- privacy: {
149
- differentialPrivacy: federation.options.differentialPrivacy || false,
150
- encryptionEnabled: federation.options.encryptionEnabled || true,
151
- dataLocality: "preserved",
152
- },
153
- };
154
- }
155
-
156
- /**
157
- * Validate participant eligibility
158
- * @param {object} participant - Participant to validate
159
- * @returns {boolean} Whether participant is valid
160
- */
161
- function validateParticipant(participant) {
162
- if (!participant || typeof participant !== "object") {
163
- return false;
164
- }
165
-
166
- // Check if participant has non-empty string id
167
- if (
168
- !participant.id ||
169
- typeof participant.id !== "string" ||
170
- participant.id.trim() === ""
171
- ) {
172
- return false;
173
- }
174
-
175
- // Check if participant is explicitly marked as ineligible
176
- if (participant.eligible === false) {
177
- return false;
178
- }
179
-
180
- return true;
181
- }
182
-
183
- /**
184
- * Clear all stored data (for testing)
185
- */
186
- function clear() {
187
- federations.clear();
188
- federationCounter = 0;
189
- eventEmitter.removeAllListeners();
190
- }
191
-
192
- // Singleton object with required methods and EventEmitter functionality
193
- const federatedLearningCoordinator = {
194
- // Core federated learning methods
195
- createFederation,
196
- registerParticipants,
197
- conductRound,
198
- getStats,
199
- validateParticipant,
200
-
201
- // EventEmitter methods
202
- on: eventEmitter.on.bind(eventEmitter),
203
- off: eventEmitter.removeListener.bind(eventEmitter),
204
- emit: eventEmitter.emit.bind(eventEmitter),
205
-
206
- // Additional utility methods
207
- clear,
208
- };
209
-
210
- // Export singleton as default
211
- module.exports = federatedLearningCoordinator;
@@ -1,71 +0,0 @@
1
- /**
2
- * Federated Learning Coordinator
3
- * Manages distributed learning across multiple participants
4
- * Extracted from ai/index.js per CLAUDE.md decomposition requirements
5
- */
6
-
7
- "use strict";
8
-
9
- // Import the existing federated learning implementation
10
- const federatedLearning = require("./federated-learning");
11
-
12
- /**
13
- * FederatedLearningCoordinator class
14
- * Coordinates federated learning processes across multiple nodes
15
- */
16
- class FederatedLearningCoordinator {
17
- constructor() {
18
- this.participants = new Map();
19
- this.rounds = new Map();
20
- this.modelVersions = new Map();
21
- this.aggregationStrategies = new Set(["fedavg", "fedprox", "scaffold"]);
22
- }
23
-
24
- /**
25
- * Start federated learning round
26
- * @param {string} tenantId - Tenant identifier
27
- * @param {object} roundConfig - Round configuration
28
- * @returns {Promise<string>} Round ID
29
- */
30
- async startRound(tenantId, roundConfig) {
31
- return await federatedLearning.startRound(tenantId, roundConfig);
32
- }
33
-
34
- /**
35
- * Register participant in federated learning
36
- * @param {string} tenantId - Tenant identifier
37
- * @param {string} participantId - Participant identifier
38
- * @param {object} capabilities - Participant capabilities
39
- * @returns {Promise<boolean>} Registration success
40
- */
41
- async registerParticipant(tenantId, participantId, capabilities) {
42
- return await federatedLearning.registerParticipant(
43
- tenantId,
44
- participantId,
45
- capabilities,
46
- );
47
- }
48
-
49
- /**
50
- * Aggregate model updates from participants
51
- * @param {string} tenantId - Tenant identifier
52
- * @param {string} roundId - Round identifier
53
- * @param {Array} updates - Model updates from participants
54
- * @returns {Promise<object>} Aggregated model
55
- */
56
- async aggregateUpdates(tenantId, roundId, updates) {
57
- return await federatedLearning.aggregateUpdates(tenantId, roundId, updates);
58
- }
59
-
60
- /**
61
- * Get federated learning progress
62
- * @param {string} tenantId - Tenant identifier
63
- * @param {string} roundId - Round identifier
64
- * @returns {Promise<object>} Progress information
65
- */
66
- async getRoundProgress(tenantId, roundId) {
67
- return await federatedLearning.getRoundProgress(tenantId, roundId);
68
- }
69
- }
70
-
71
- module.exports = { FederatedLearningCoordinator };
package/src/ai/index.js DELETED
@@ -1,22 +0,0 @@
1
- /**
2
- * AI/ML Module Index - Decomposed Architecture
3
- * Provides unified interface to 4 separate AI modules
4
- *
5
- * Decomposed modules:
6
- * - orchestrator.js - ModelTrainingOrchestrator
7
- * - retrieval-engine.js - AdaptiveRetrievalEngine
8
- * - multimodal.js - MultiModalProcessor
9
- * - federation.js - FederatedLearningCoordinator
10
- */
11
-
12
- const { ModelTrainingOrchestrator } = require("./orchestrator");
13
- const { AdaptiveRetrievalEngine } = require("./retrieval-engine");
14
- const { MultiModalProcessor } = require("./multimodal");
15
- const { FederatedLearningCoordinator } = require("./federation");
16
-
17
- module.exports = {
18
- ModelTrainingOrchestrator,
19
- AdaptiveRetrievalEngine,
20
- MultiModalProcessor,
21
- FederatedLearningCoordinator,
22
- };