@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.
- package/.ragrc.schema.json +83 -192
- package/CHANGELOG.md +431 -0
- package/README.md +418 -117
- package/SECURITY.md +5 -5
- package/dist/experimental.cjs +2 -0
- package/dist/experimental.d.ts +1 -0
- package/dist/experimental.mjs +5 -0
- package/dist/index.cjs +6442 -1355
- package/dist/index.d.ts +129 -2
- package/dist/index.mjs +1 -1
- package/dist/testing.cjs +2 -0
- package/dist/testing.d.ts +1 -0
- package/dist/testing.mjs +5 -0
- package/dist/types/adapters/ai-sdk.d.ts +53 -0
- package/dist/types/agentic/agentic-pipeline.d.ts +103 -0
- package/dist/types/agentic/iterative-retriever.d.ts +85 -0
- package/dist/types/agentic/query-planner.d.ts +145 -0
- package/dist/types/agentic/self-critique.d.ts +45 -0
- package/dist/types/ai/adaptive-retrieval.d.ts +44 -0
- package/dist/types/ai/federation/federated-learning-coordinator.d.ts +62 -0
- package/dist/types/ai/multimodal-processing.d.ts +32 -0
- package/dist/types/ai/multimodal.d.ts +39 -0
- package/dist/types/ai/retrieval-engine.d.ts +37 -0
- package/dist/types/ai/training/model-training-orchestrator.d.ts +31 -0
- package/dist/types/cache/component-keys.d.ts +7 -0
- package/dist/types/cache/memory-cache.d.ts +54 -0
- package/dist/types/cache/pipeline-cache.d.ts +66 -0
- package/dist/types/cache/redis-cache.d.ts +46 -0
- package/dist/types/chunking/chunking-engine.d.ts +70 -0
- package/dist/types/chunking/strategies/fixed-size.d.ts +17 -0
- package/dist/types/chunking/strategies/recursive.d.ts +20 -0
- package/dist/types/chunking/strategies/semantic.d.ts +19 -0
- package/dist/types/chunking/strategies/sentence.d.ts +13 -0
- package/dist/types/chunking/strategies/structure-aware.d.ts +19 -0
- package/dist/types/citation/citation-tracker.d.ts +64 -0
- package/dist/types/citation/hallucination-detector.d.ts +40 -0
- package/dist/types/citation/source-mapper.d.ts +37 -0
- package/dist/types/connectors/anthropic-connector.d.ts +97 -0
- package/dist/types/connectors/base-connector.d.ts +87 -0
- package/dist/types/connectors/cohere-connector.d.ts +94 -0
- package/dist/types/connectors/connector-utils.d.ts +110 -0
- package/dist/types/connectors/local-embedder.d.ts +95 -0
- package/dist/types/connectors/memory-retriever.d.ts +99 -0
- package/dist/types/connectors/ollama-connector.d.ts +115 -0
- package/dist/types/connectors/openai-connector.d.ts +129 -0
- package/dist/types/connectors/rag-prompt.d.ts +40 -0
- package/dist/types/core/create-pipeline.d.ts +8 -0
- package/dist/types/core/plugin-registry.d.ts +136 -0
- package/dist/types/core/stage-contracts.d.ts +36 -0
- package/dist/types/core/validate-plugin.d.ts +45 -0
- package/dist/types/cost/cost-calculator.d.ts +152 -0
- package/dist/types/cost/cost-tracker.d.ts +126 -0
- package/dist/types/cost/token-budget.d.ts +110 -0
- package/dist/types/dag/core/error-context.d.ts +239 -0
- package/dist/types/dag/core/execution-scheduler.d.ts +191 -0
- package/dist/types/dag/core/topology-validation.d.ts +226 -0
- package/dist/types/dag/dag-engine.d.ts +156 -0
- package/dist/types/dag/dag-node.d.ts +37 -0
- package/dist/types/dashboard/dashboard-generator.d.ts +127 -0
- package/dist/types/dashboard/metrics-aggregator.d.ts +218 -0
- package/dist/types/debug/execution-tracer.d.ts +57 -0
- package/dist/types/debug/trace-inspector.d.ts +31 -0
- package/dist/types/ecosystem/plugin-certification.d.ts +169 -0
- package/dist/types/enterprise/data-governance.d.ts +203 -0
- package/dist/types/enterprise/multi-tenancy.d.ts +137 -0
- package/dist/types/enterprise/sso-integration.d.ts +420 -0
- package/dist/types/evaluate/context-metrics.d.ts +55 -0
- package/dist/types/evaluate/faithfulness.d.ts +41 -0
- package/dist/types/evaluate/groundedness.d.ts +60 -0
- package/dist/types/evaluate/pipeline-evaluator.d.ts +88 -0
- package/dist/types/evaluate/relevance.d.ts +21 -0
- package/dist/types/evaluate/scoring.d.ts +123 -0
- package/dist/types/evaluate/semantic-similarity.d.ts +14 -0
- package/dist/types/experimental.d.ts +15 -0
- package/dist/types/graph/entity-extractor.d.ts +84 -0
- package/dist/types/graph/graph-index.d.ts +49 -0
- package/dist/types/graph/graph-retriever.d.ts +81 -0
- package/dist/types/graph/knowledge-graph.d.ts +129 -0
- package/dist/types/grounding/check-answer.d.ts +101 -0
- package/dist/types/guardrails/guardrails-pipeline.d.ts +63 -0
- package/dist/types/guardrails/injection-detector.d.ts +47 -0
- package/dist/types/guardrails/post-generation-guard.d.ts +61 -0
- package/dist/types/guardrails/pre-retrieval-guard.d.ts +64 -0
- package/dist/types/guardrails/retrieval-guard.d.ts +57 -0
- package/dist/types/mcp/mcp-server.d.ts +85 -0
- package/dist/types/mcp/mcp-tool-builder.d.ts +55 -0
- package/dist/types/reranker/cascade-reranker.d.ts +40 -0
- package/dist/types/reranker/embedding-reranker.d.ts +77 -0
- package/dist/types/reranker/scoring-reranker.d.ts +41 -0
- package/dist/types/retrieval/bm25-search.d.ts +43 -0
- package/dist/types/retrieval/file-vector-store.d.ts +99 -0
- package/dist/types/retrieval/hybrid-retriever.d.ts +35 -0
- package/dist/types/retrieval/rank-fusion.d.ts +10 -0
- package/dist/types/security/jwt-config.d.ts +80 -0
- package/dist/types/security/jwt-validator.d.ts +195 -0
- package/dist/types/security/plugin-sandbox.d.ts +158 -0
- package/dist/types/security/plugin-signature-verifier.d.ts +139 -0
- package/dist/types/security/validator.d.ts +5 -0
- package/dist/types/streaming/embedding-stream.d.ts +65 -0
- package/dist/types/streaming/sse-adapter.d.ts +59 -0
- package/dist/types/streaming/stream-router.d.ts +88 -0
- package/dist/types/streaming/streaming-embedder.d.ts +99 -0
- package/dist/types/streaming/websocket-adapter.d.ts +71 -0
- package/dist/types/templates/scaffold.d.ts +55 -0
- package/dist/types/templates/template-registry.d.ts +63 -0
- package/dist/types/testing.d.ts +168 -0
- package/dist/types/utils/embed-query.d.ts +20 -0
- package/dist/types/utils/logger.d.ts +50 -0
- package/dist/types/utils/rate-limiter.d.ts +66 -0
- package/dist/types/utils/retry.d.ts +27 -0
- package/dist/types/utils/run-abort.d.ts +34 -0
- package/dist/types/utils/secure-logging.d.ts +51 -0
- package/dist/types/utils/structured-logger.d.ts +126 -0
- package/package.json +72 -33
- package/src/adapters/ai-sdk.js +215 -0
- package/src/agentic/agentic-pipeline.js +75 -7
- package/src/agentic/iterative-retriever.js +47 -10
- package/src/agentic/query-planner.js +329 -78
- package/src/cache/component-keys.js +38 -0
- package/src/cache/memory-cache.js +87 -0
- package/src/cache/pipeline-cache.js +260 -0
- package/src/cache/redis-cache.js +83 -0
- package/src/citation/citation-tracker.js +6 -4
- package/src/citation/hallucination-detector.js +2 -2
- package/src/citation/source-mapper.js +15 -28
- package/src/cli/commands/ai.js +106 -0
- package/src/cli/commands/plugin-hub/event-handlers.js +6 -0
- package/src/cli/commands/plugin-hub/hub-commands.js +0 -74
- package/src/cli/commands/plugin-hub/local-commands.js +143 -0
- package/src/cli/commands/plugin-hub.js +114 -49
- package/src/cli/diagnostics/config-checker.js +76 -63
- package/src/cli/diagnostics/environment-checker.js +62 -31
- package/src/cli/diagnostics/plugin-checker.js +36 -89
- package/src/cli/doctor-command.js +39 -5
- package/src/cli/enhanced-cli-commands.js +104 -233
- package/src/cli/handlers/ai-handler.js +364 -0
- package/src/cli/handlers/diagnostics-handler.js +78 -31
- package/src/cli/handlers/ingest-handler.js +28 -22
- package/src/cli/handlers/init-handler.js +148 -25
- package/src/cli/handlers/query-handler.js +203 -25
- package/src/cli/handlers/run-handler.js +16 -167
- package/src/cli/handlers/serve-handler.js +60 -0
- package/src/cli/services/bench-stats.js +54 -0
- package/src/cli/services/benchmark.js +161 -0
- package/src/cli/services/feedback.js +155 -0
- package/src/cli/services/instrument.js +83 -0
- package/src/cli/services/rag-service.js +361 -0
- package/src/cli/shared/cli-utils.js +24 -7
- package/src/cli/shared/config-context.js +21 -0
- package/src/cli/wizard/config-builder.js +81 -0
- package/src/cli/wizard/init-wizard.js +602 -0
- package/src/config/component-factory.js +125 -0
- package/src/config/enhanced-ragrc-schema.js +0 -2
- package/src/config/load-config.js +29 -10
- package/src/config/load-plugin-config.js +46 -2
- package/src/config/validate-schema.js +4 -1
- package/src/connectors/anthropic-connector.js +115 -57
- package/src/connectors/base-connector.js +74 -19
- package/src/connectors/cohere-connector.js +108 -34
- package/src/connectors/connector-utils.js +302 -0
- package/src/connectors/local-embedder.js +63 -5
- package/src/connectors/memory-retriever.js +125 -14
- package/src/connectors/ollama-connector.js +112 -51
- package/src/connectors/openai-connector.js +153 -44
- package/src/connectors/rag-prompt.js +88 -0
- package/src/core/create-pipeline.js +307 -61
- package/src/core/performance/parallel-processor.js +62 -1
- package/src/core/plugin-registry.js +125 -26
- package/src/core/stage-contracts.js +36 -0
- package/src/core/validate-plugin.js +85 -0
- package/src/cost/cost-calculator.js +205 -32
- package/src/debug/execution-tracer.js +14 -4
- package/src/dev/dev-server.js +173 -11
- package/src/dev/hot-reload.js +44 -4
- package/src/ecosystem/plugin-checks.js +181 -0
- package/src/ecosystem/plugin-hub.js +50 -125
- package/src/ecosystem/plugin-installer.js +298 -0
- package/src/ecosystem/plugin-probe.js +127 -0
- package/src/ecosystem/plugin-scanner.js +185 -0
- package/src/ecosystem/tar-extract.js +114 -0
- package/src/enterprise/audit-logging.js +158 -29
- package/src/enterprise/sso-integration.js +716 -228
- package/src/evaluate/context-metrics.js +28 -16
- package/src/evaluate/evaluator.js +3 -3
- package/src/evaluate/faithfulness.js +18 -14
- package/src/evaluate/groundedness.js +43 -12
- package/src/evaluate/pipeline-evaluator.js +35 -8
- package/src/evaluate/scoring.js +251 -3
- package/src/evaluate/semantic-similarity.js +79 -0
- package/src/experimental.js +71 -0
- package/src/grounding/check-answer.js +173 -0
- package/src/guardrails/guardrails-pipeline.js +69 -7
- package/src/guardrails/injection-detector.js +283 -0
- package/src/guardrails/post-generation-guard.js +19 -0
- package/src/guardrails/pre-retrieval-guard.js +36 -36
- package/src/guardrails/retrieval-guard.js +58 -8
- package/src/index.js +58 -1
- package/src/loader/csv-loader.js +3 -2
- package/src/loader/document-reader.js +91 -0
- package/src/loader/html-loader.js +3 -1
- package/src/loader/markdown-loader.js +3 -2
- package/src/reranker/cascade-reranker.js +3 -0
- package/src/reranker/embedding-reranker.js +136 -21
- package/src/reranker/llm-reranker.js +79 -12
- package/src/reranker/scoring-reranker.js +3 -0
- package/src/retrieval/bm25-search.js +12 -6
- package/src/retrieval/file-vector-store.js +241 -0
- package/src/retrieval/hybrid-retriever.js +22 -19
- package/src/security/jwt-config.js +13 -2
- package/src/security/jwt-validator.js +139 -34
- package/src/security/plugin-sandbox.js +18 -1
- package/src/security/plugin-signature-verifier.js +245 -70
- package/src/testing.js +254 -0
- package/src/utils/deprecate.js +69 -0
- package/src/utils/embed-query.js +33 -0
- package/src/utils/input-sanitizer.js +19 -4
- package/src/utils/logger.js +34 -4
- package/src/utils/run-abort.js +102 -0
- package/src/ai/federated-learning.js +0 -211
- package/src/ai/federation.js +0 -71
- package/src/ai/index.js +0 -22
- package/src/ai/model-trainer.js +0 -217
- package/src/ai/model-training.js +0 -202
- package/src/ai/multimodal/multi-modal-processor.js +0 -345
- package/src/ai/orchestrator.js +0 -65
- package/src/ai/retrieval/adaptive-retrieval-engine.js +0 -398
- package/src/cli/commands/ai-ml/adaptive-commands.js +0 -178
- package/src/cli/commands/ai-ml/federated-commands.js +0 -207
- package/src/cli/commands/ai-ml/index.js +0 -17
- package/src/cli/commands/ai-ml/multimodal-commands.js +0 -187
- package/src/cli/commands/ai-ml/shared.js +0 -51
- package/src/cli/commands/ai-ml/training-commands.js +0 -228
- package/src/cli/commands/ai-ml.js +0 -386
- package/src/cli/commands/docs.js +0 -228
- package/src/cli/commands/dx.js +0 -538
- package/src/cli/commands/wizard.js +0 -627
- package/src/cli/input-sanitization-middleware.js +0 -356
- package/src/cli/interactive-wizard.js +0 -373
- package/src/cli/templates/development.template.json +0 -64
- package/src/cli/templates/minimal.template.json +0 -33
- package/src/cli/templates/production.template.json +0 -104
- package/src/cli/templates/testing.template.json +0 -45
- package/src/cli/wizard/configuration.js +0 -148
- package/src/cli/wizard/observability.js +0 -79
- package/src/cli/wizard/performance.js +0 -76
- package/src/cli/wizard/plugin-selection.js +0 -297
- package/src/cli/wizard/preview-save.js +0 -172
- package/src/cli/wizard/project-setup.js +0 -66
- package/src/core/pipeline-factory.js +0 -59
- package/src/dag/enhanced-dag-engine.js +0 -894
- package/src/dx/index.js +0 -40
- package/src/dx/integration-templates.js +0 -662
- package/src/dx/performance-profiler.js +0 -968
- package/src/dx/realtime-debugger.js +0 -755
- package/src/dx/visual-pipeline-builder.js +0 -578
- package/src/ingest.js +0 -59
- package/src/pipeline/pipeline.js +0 -18
- 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
|
|
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
|
|
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
|
|
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
|
|
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
|
package/src/utils/logger.js
CHANGED
|
@@ -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
|
-
|
|
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;
|
package/src/ai/federation.js
DELETED
|
@@ -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
|
-
};
|