@devilsdev/rag-pipeline-utils 2.1.12 → 2.2.1
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 +218 -0
- package/README.md +178 -550
- package/bin/cli.js +8 -14
- package/contracts/reranker-contract.json +69 -0
- package/dist/index.d.ts +237 -0
- package/dist/index.mjs +22 -7
- package/package.json +18 -13
- package/src/ai/adaptive-retrieval.js +191 -0
- package/src/ai/federated-learning.js +214 -0
- package/src/ai/federation/federated-learning-coordinator.js +345 -0
- package/src/ai/federation.js +76 -0
- package/src/ai/index-fixed.js +509 -0
- package/src/ai/index.js +33 -0
- package/src/ai/model-trainer.js +217 -0
- package/src/ai/model-training.js +205 -0
- package/src/ai/module-template.js +37 -0
- package/src/ai/multimodal/multi-modal-processor.js +345 -0
- package/src/ai/multimodal-processing.js +268 -0
- package/src/ai/multimodal.js +71 -0
- package/src/ai/orchestrator.js +70 -0
- package/src/ai/retrieval/adaptive-retrieval-engine.js +398 -0
- package/src/ai/retrieval-engine.js +69 -0
- package/src/ai/training/model-training-orchestrator.js +228 -0
- package/src/ci/workflow-analyzer.js +71 -0
- package/src/cli/commands/ai-ml.js +967 -0
- package/src/cli/commands/docs.js +228 -0
- package/src/cli/commands/dx.js +538 -0
- package/src/cli/commands/plugin-hub.js +875 -0
- package/src/cli/doctor-command.js +639 -0
- package/src/cli/enhanced-cli-commands.js +945 -0
- package/src/cli/interactive-wizard.js +997 -0
- package/src/cli/plugin-marketplace-commands.js +869 -0
- package/src/cli.js +17 -0
- package/src/config/enhanced-ragrc-schema.js +632 -0
- package/src/config/load-config.js +54 -0
- package/src/config/load-plugin-config.js +69 -0
- package/src/config/normalize-config.js +105 -0
- package/src/config/schema-validator.js +315 -0
- package/src/config/validate-plugin-schema.js +35 -0
- package/src/config/validate-schema.js +113 -0
- package/src/contracts/plugin-contracts.js +33 -0
- package/src/core/create-pipeline.js +99 -0
- package/src/core/dependency-injection.js +242 -0
- package/src/core/observability/event-logger.js +241 -0
- package/src/core/observability/instrumented-pipeline.js +609 -0
- package/src/core/observability/metrics.js +497 -0
- package/src/core/observability/tracing.js +781 -0
- package/src/core/performance/benchmark.js +516 -0
- package/src/core/performance/parallel-processor.js +240 -0
- package/src/core/performance/streaming-safeguards.js +620 -0
- package/src/core/pipeline-factory.js +60 -0
- package/src/core/plugin-contracts.js +89 -0
- package/src/core/plugin-marketplace/plugin-metadata.js +485 -0
- package/src/core/plugin-marketplace/plugin-publisher.js +574 -0
- package/src/core/plugin-marketplace/plugin-registry-format.js +419 -0
- package/src/core/plugin-marketplace/version-resolver.js +61 -0
- package/src/core/plugin-registry.js +294 -0
- package/src/dag/dag-engine.js +954 -0
- package/src/dag/dag-node.js +76 -0
- package/src/dag/enhanced-dag-engine.js +822 -0
- package/src/dx/index.js +40 -0
- package/src/dx/integration-templates.js +665 -0
- package/src/dx/performance-profiler.js +968 -0
- package/src/dx/realtime-debugger.js +755 -0
- package/src/dx/visual-pipeline-builder.js +578 -0
- package/src/ecosystem/plugin-analytics-dashboard.js +778 -0
- package/src/ecosystem/plugin-certification.js +580 -0
- package/src/ecosystem/plugin-hub.js +821 -0
- package/src/enterprise/audit-logging.js +967 -0
- package/src/enterprise/data-governance.js +408 -0
- package/src/enterprise/multi-tenancy.js +496 -0
- package/src/enterprise/sso-integration.js +1030 -0
- package/src/evaluate/evaluator.js +52 -0
- package/src/evaluate/scoring.js +91 -0
- package/src/index.js +67 -0
- package/src/ingest.js +59 -0
- package/src/loader/csv-loader.js +38 -0
- package/src/loader/directory-loader.js +48 -0
- package/src/loader/html-loader.js +62 -0
- package/src/loader/markdown-loader.js +67 -0
- package/src/mocks/openai-embedder.js +119 -0
- package/src/mocks/openai-llm.js +77 -0
- package/src/mocks/pdf-loader.js +21 -0
- package/src/mocks/pinecone-retriever.js +106 -0
- package/src/observability/audit-logger.js +758 -0
- package/src/observability/dashboard-config.json +675 -0
- package/src/observability/event-logger.js +310 -0
- package/src/observability/metrics.js +691 -0
- package/src/observability/security-monitor.js +830 -0
- package/src/observability/slo-monitor.js +319 -0
- package/src/observability/telemetry.js +686 -0
- package/src/pipeline/pipeline.js +18 -0
- package/src/plugins/sample-embedder.js +53 -0
- package/src/plugins/sample-llm.js +30 -0
- package/src/plugins/sample-loader.js +24 -0
- package/src/plugins/sample-retriever.js +84 -0
- package/src/query.js +117 -0
- package/src/reranker/llm-reranker.js +111 -0
- package/src/scripts/script-utilities.js +36 -0
- package/src/security/plugin-sandbox.js +586 -0
- package/src/security/plugin-signature-verifier.js +414 -0
- package/src/security/validator.js +36 -0
- package/src/utils/ci/diagnostic-reporter.js +57 -0
- package/src/utils/logger.js +211 -0
- package/src/utils/performance-monitor.js +561 -0
- package/src/utils/plugin-scaffolder.js +447 -0
- package/src/utils/rate-limiter.js +244 -0
- package/src/utils/retry-policy.js +443 -0
- package/src/utils/retry.js +107 -0
- package/src/utils/safe-expression-evaluator.js +579 -0
- package/src/utils/secure-logging.js +338 -0
- package/src/utils/structured-logger.js +388 -0
- package/src/utils/validate-plugin-contract.js +42 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Version: 1.1.1
|
|
3
|
+
* Description: Dynamic plugin loader with direct _type:name registry mapping
|
|
4
|
+
* Author: Ali Kahwaji
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require("fs");
|
|
8
|
+
// eslint-disable-line global-require
|
|
9
|
+
const path = require("path");
|
|
10
|
+
// eslint-disable-line global-require
|
|
11
|
+
const { pathToFileURL } = require("url");
|
|
12
|
+
// eslint-disable-line global-require
|
|
13
|
+
const registry = require("../core/plugin-registry.js");
|
|
14
|
+
// eslint-disable-line global-require
|
|
15
|
+
const { validatePluginSchema } = require("./validate-schema.js");
|
|
16
|
+
// eslint-disable-line global-require
|
|
17
|
+
const { logger } = require("../utils/logger.js");
|
|
18
|
+
// eslint-disable-line global-require
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Loads plugins from validated _config path and registers them.
|
|
22
|
+
* @param {string} configPath
|
|
23
|
+
*/
|
|
24
|
+
async function loadPluginsFromJson(configPath) {
|
|
25
|
+
if (!fs.existsSync(configPath)) {
|
|
26
|
+
throw new Error(`Plugin _config missing: ${configPath}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
30
|
+
const _config = JSON.parse(raw);
|
|
31
|
+
|
|
32
|
+
const validation = validatePluginSchema(_config);
|
|
33
|
+
if (!validation.valid) {
|
|
34
|
+
logger.error({ errors: validation.errors }, "Invalid plugin _config");
|
|
35
|
+
throw new Error("Plugin _config validation failed");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
for (const [_type, namedPlugins] of Object.entries(_config)) {
|
|
39
|
+
if (!["loader", "embedder", "retriever", "llm"].includes(_type)) continue;
|
|
40
|
+
|
|
41
|
+
for (const [name, relPath] of Object.entries(namedPlugins)) {
|
|
42
|
+
const absPath = path.resolve(path.dirname(configPath), relPath);
|
|
43
|
+
const fileUrl = pathToFileURL(absPath).href;
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const mod = await import(fileUrl);
|
|
47
|
+
const PluginClass = mod?.default;
|
|
48
|
+
if (typeof PluginClass !== "function") {
|
|
49
|
+
throw new Error(
|
|
50
|
+
`Invalid export in plugin [${_type}:${name}] at ${absPath}`,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const _instance = new PluginClass();
|
|
55
|
+
registry.register(_type, name, _instance);
|
|
56
|
+
|
|
57
|
+
logger.info({ _type, name }, `Registered plugin: ${_type}:${name}`);
|
|
58
|
+
} catch (err) {
|
|
59
|
+
logger.error(
|
|
60
|
+
{ _type, name, err },
|
|
61
|
+
`Failed to register plugin [${_type}:${name}]`,
|
|
62
|
+
);
|
|
63
|
+
throw err;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = { loadPluginsFromJson };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Normalization - Unifies config shape and validation
|
|
3
|
+
* Handles pipeline object/array conversion and default namespace assignment
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
function normalizeConfig(cfg) {
|
|
7
|
+
if (!cfg || typeof cfg !== "object" || Array.isArray(cfg)) {
|
|
8
|
+
throw new TypeError("Configuration must be a non-null object");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Track validation warnings for invalid entries that get filtered out
|
|
12
|
+
const warnings = [];
|
|
13
|
+
|
|
14
|
+
// Default namespace
|
|
15
|
+
const namespace = cfg.namespace || "default";
|
|
16
|
+
|
|
17
|
+
// Canonical pipeline: array of { stage, name, ...opts }
|
|
18
|
+
// Accept legacy object form like:
|
|
19
|
+
// { pipeline: { loader:{name:'x'}, embedder:{name:'y'} } }
|
|
20
|
+
// { plugins: { loader:'file-loader', embedder:'openai-embedder' } }
|
|
21
|
+
// and modern array form:
|
|
22
|
+
// { pipeline: [{stage:'loader', name:'x'}] }
|
|
23
|
+
const legacy =
|
|
24
|
+
(cfg.pipeline &&
|
|
25
|
+
!Array.isArray(cfg.pipeline) &&
|
|
26
|
+
typeof cfg.pipeline === "object") ||
|
|
27
|
+
(cfg.plugins && typeof cfg.plugins === "object");
|
|
28
|
+
const modern = Array.isArray(cfg.pipeline);
|
|
29
|
+
|
|
30
|
+
let pipeline = [];
|
|
31
|
+
if (modern) {
|
|
32
|
+
pipeline = cfg.pipeline
|
|
33
|
+
.filter(Boolean)
|
|
34
|
+
.map((p) => {
|
|
35
|
+
const entry = { stage: p.stage, name: p.name };
|
|
36
|
+
if (p.version) entry.version = p.version;
|
|
37
|
+
if (p.config) entry.config = p.config;
|
|
38
|
+
if (p.options) Object.assign(entry, p.options);
|
|
39
|
+
return entry;
|
|
40
|
+
})
|
|
41
|
+
.filter((p) => typeof p.stage === "string" && typeof p.name === "string");
|
|
42
|
+
} else if (legacy) {
|
|
43
|
+
const order = ["loader", "embedder", "retriever", "reranker", "llm"];
|
|
44
|
+
const source = cfg.pipeline || cfg.plugins;
|
|
45
|
+
|
|
46
|
+
for (const stage of order) {
|
|
47
|
+
const entry = source[stage];
|
|
48
|
+
if (entry) {
|
|
49
|
+
if (typeof entry === "string") {
|
|
50
|
+
// Simple format: { plugins: { loader: 'file-loader' } }
|
|
51
|
+
pipeline.push({ stage, name: entry });
|
|
52
|
+
} else if (
|
|
53
|
+
entry &&
|
|
54
|
+
typeof entry === "object" &&
|
|
55
|
+
typeof entry.name === "string"
|
|
56
|
+
) {
|
|
57
|
+
// Complex format: { pipeline: { loader: { name: 'file-loader', version: '1.0.0', config: {}, options: {} } } }
|
|
58
|
+
const pipelineEntry = { stage, name: entry.name };
|
|
59
|
+
if (entry.version) pipelineEntry.version = entry.version;
|
|
60
|
+
if (entry.config) pipelineEntry.config = entry.config;
|
|
61
|
+
if (entry.options) Object.assign(pipelineEntry, entry.options);
|
|
62
|
+
pipeline.push(pipelineEntry);
|
|
63
|
+
} else if (entry && typeof entry === "object") {
|
|
64
|
+
// Invalid entry - has object but invalid name
|
|
65
|
+
if (typeof entry.name !== "string") {
|
|
66
|
+
warnings.push(
|
|
67
|
+
`Invalid ${stage} plugin name: expected string, got ${typeof entry.name}`,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Ensure at least handles loader/embedder for legacy tests
|
|
76
|
+
// (empty array is allowed but tests expect 2 when legacy provided)
|
|
77
|
+
const metadata =
|
|
78
|
+
cfg.metadata && typeof cfg.metadata === "object" ? cfg.metadata : {};
|
|
79
|
+
|
|
80
|
+
const out = {
|
|
81
|
+
namespace,
|
|
82
|
+
metadata,
|
|
83
|
+
pipeline,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// Preserve known top-level fields (non-breaking)
|
|
87
|
+
const known = [
|
|
88
|
+
"cache",
|
|
89
|
+
"limits",
|
|
90
|
+
"plugins",
|
|
91
|
+
"observability",
|
|
92
|
+
"storage",
|
|
93
|
+
"performance",
|
|
94
|
+
];
|
|
95
|
+
for (const k of known) if (k in cfg) out[k] = cfg[k];
|
|
96
|
+
|
|
97
|
+
// Add warnings if any invalid entries were encountered
|
|
98
|
+
if (warnings.length > 0) {
|
|
99
|
+
out._warnings = warnings;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
module.exports = { normalizeConfig };
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Schema Validator - Enterprise-grade config validation
|
|
3
|
+
* Provides JSON Schema validation for all configuration files
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const Ajv = require("ajv");
|
|
7
|
+
const addFormats = require("ajv-formats");
|
|
8
|
+
const { logger } = require("../utils/logger");
|
|
9
|
+
|
|
10
|
+
class ConfigSchemaValidator {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.ajv = new Ajv({
|
|
13
|
+
allErrors: true,
|
|
14
|
+
verbose: true,
|
|
15
|
+
strict: false,
|
|
16
|
+
});
|
|
17
|
+
addFormats(this.ajv);
|
|
18
|
+
|
|
19
|
+
// Register custom formats
|
|
20
|
+
this.ajv.addFormat("semver", /^\d+\.\d+\.\d+(-[\w.-]+)?(\+[\w.-]+)?$/);
|
|
21
|
+
this.ajv.addFormat("log-level", /^(trace|debug|info|warn|error|fatal)$/);
|
|
22
|
+
|
|
23
|
+
this.schemas = new Map();
|
|
24
|
+
this._loadSchemas();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
_loadSchemas() {
|
|
28
|
+
// Core pipeline configuration schema
|
|
29
|
+
const pipelineSchema = {
|
|
30
|
+
type: "object",
|
|
31
|
+
properties: {
|
|
32
|
+
version: { type: "string", format: "semver" },
|
|
33
|
+
name: { type: "string", minLength: 1 },
|
|
34
|
+
description: { type: "string" },
|
|
35
|
+
pipeline: {
|
|
36
|
+
type: "object",
|
|
37
|
+
properties: {
|
|
38
|
+
steps: {
|
|
39
|
+
type: "array",
|
|
40
|
+
items: {
|
|
41
|
+
type: "object",
|
|
42
|
+
properties: {
|
|
43
|
+
name: { type: "string", minLength: 1 },
|
|
44
|
+
type: {
|
|
45
|
+
type: "string",
|
|
46
|
+
enum: [
|
|
47
|
+
"ingest",
|
|
48
|
+
"chunk",
|
|
49
|
+
"embed",
|
|
50
|
+
"store",
|
|
51
|
+
"query",
|
|
52
|
+
"evaluate",
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
config: { type: "object" },
|
|
56
|
+
enabled: { type: "boolean", default: true },
|
|
57
|
+
},
|
|
58
|
+
required: ["name", "type"],
|
|
59
|
+
additionalProperties: false,
|
|
60
|
+
},
|
|
61
|
+
minItems: 1,
|
|
62
|
+
},
|
|
63
|
+
concurrency: {
|
|
64
|
+
type: "integer",
|
|
65
|
+
minimum: 1,
|
|
66
|
+
maximum: 100,
|
|
67
|
+
default: 5,
|
|
68
|
+
},
|
|
69
|
+
timeout: { type: "integer", minimum: 1000, default: 30000 },
|
|
70
|
+
retries: { type: "integer", minimum: 0, maximum: 10, default: 3 },
|
|
71
|
+
},
|
|
72
|
+
required: ["steps"],
|
|
73
|
+
additionalProperties: false,
|
|
74
|
+
},
|
|
75
|
+
logging: {
|
|
76
|
+
type: "object",
|
|
77
|
+
properties: {
|
|
78
|
+
level: { type: "string", format: "log-level", default: "info" },
|
|
79
|
+
structured: { type: "boolean", default: true },
|
|
80
|
+
correlationId: { type: "boolean", default: true },
|
|
81
|
+
},
|
|
82
|
+
additionalProperties: false,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
required: ["version", "name", "pipeline"],
|
|
86
|
+
additionalProperties: false,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// AI/ML configuration schema
|
|
90
|
+
const aiConfigSchema = {
|
|
91
|
+
type: "object",
|
|
92
|
+
properties: {
|
|
93
|
+
embeddings: {
|
|
94
|
+
type: "object",
|
|
95
|
+
properties: {
|
|
96
|
+
provider: {
|
|
97
|
+
type: "string",
|
|
98
|
+
enum: ["openai", "azure", "huggingface", "local"],
|
|
99
|
+
},
|
|
100
|
+
model: { type: "string", minLength: 1 },
|
|
101
|
+
dimensions: { type: "integer", minimum: 1, maximum: 4096 },
|
|
102
|
+
batchSize: {
|
|
103
|
+
type: "integer",
|
|
104
|
+
minimum: 1,
|
|
105
|
+
maximum: 1000,
|
|
106
|
+
default: 100,
|
|
107
|
+
},
|
|
108
|
+
timeout: { type: "integer", minimum: 1000, default: 30000 },
|
|
109
|
+
},
|
|
110
|
+
required: ["provider", "model"],
|
|
111
|
+
additionalProperties: false,
|
|
112
|
+
},
|
|
113
|
+
training: {
|
|
114
|
+
type: "object",
|
|
115
|
+
properties: {
|
|
116
|
+
batchSize: {
|
|
117
|
+
type: "integer",
|
|
118
|
+
minimum: 1,
|
|
119
|
+
maximum: 1024,
|
|
120
|
+
default: 32,
|
|
121
|
+
},
|
|
122
|
+
learningRate: {
|
|
123
|
+
type: "number",
|
|
124
|
+
minimum: 0.0001,
|
|
125
|
+
maximum: 1,
|
|
126
|
+
default: 0.001,
|
|
127
|
+
},
|
|
128
|
+
epochs: { type: "integer", minimum: 1, maximum: 1000, default: 10 },
|
|
129
|
+
validationSplit: {
|
|
130
|
+
type: "number",
|
|
131
|
+
minimum: 0,
|
|
132
|
+
maximum: 0.5,
|
|
133
|
+
default: 0.2,
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
additionalProperties: false,
|
|
137
|
+
},
|
|
138
|
+
federation: {
|
|
139
|
+
type: "object",
|
|
140
|
+
properties: {
|
|
141
|
+
minParticipants: {
|
|
142
|
+
type: "integer",
|
|
143
|
+
minimum: 2,
|
|
144
|
+
maximum: 1000,
|
|
145
|
+
default: 2,
|
|
146
|
+
},
|
|
147
|
+
maxParticipants: {
|
|
148
|
+
type: "integer",
|
|
149
|
+
minimum: 2,
|
|
150
|
+
maximum: 10000,
|
|
151
|
+
default: 100,
|
|
152
|
+
},
|
|
153
|
+
roundDuration: { type: "integer", minimum: 60000, default: 300000 },
|
|
154
|
+
convergenceThreshold: {
|
|
155
|
+
type: "number",
|
|
156
|
+
minimum: 0.0001,
|
|
157
|
+
maximum: 1,
|
|
158
|
+
default: 0.001,
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
additionalProperties: false,
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
additionalProperties: false,
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// Security configuration schema
|
|
168
|
+
const securitySchema = {
|
|
169
|
+
type: "object",
|
|
170
|
+
properties: {
|
|
171
|
+
authentication: {
|
|
172
|
+
type: "object",
|
|
173
|
+
properties: {
|
|
174
|
+
enabled: { type: "boolean", default: true },
|
|
175
|
+
provider: {
|
|
176
|
+
type: "string",
|
|
177
|
+
enum: ["jwt", "oauth2", "api-key", "none"],
|
|
178
|
+
},
|
|
179
|
+
tokenExpiry: { type: "integer", minimum: 300, default: 3600 },
|
|
180
|
+
},
|
|
181
|
+
required: ["enabled", "provider"],
|
|
182
|
+
additionalProperties: false,
|
|
183
|
+
},
|
|
184
|
+
encryption: {
|
|
185
|
+
type: "object",
|
|
186
|
+
properties: {
|
|
187
|
+
algorithm: {
|
|
188
|
+
type: "string",
|
|
189
|
+
enum: ["aes-256-gcm", "aes-256-cbc", "chacha20-poly1305"],
|
|
190
|
+
},
|
|
191
|
+
keyRotation: { type: "boolean", default: true },
|
|
192
|
+
keyRotationInterval: {
|
|
193
|
+
type: "integer",
|
|
194
|
+
minimum: 86400,
|
|
195
|
+
default: 2592000,
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
additionalProperties: false,
|
|
199
|
+
},
|
|
200
|
+
audit: {
|
|
201
|
+
type: "object",
|
|
202
|
+
properties: {
|
|
203
|
+
enabled: { type: "boolean", default: true },
|
|
204
|
+
retention: { type: "integer", minimum: 86400, default: 7776000 },
|
|
205
|
+
sensitiveFields: {
|
|
206
|
+
type: "array",
|
|
207
|
+
items: { type: "string" },
|
|
208
|
+
default: ["password", "token", "key", "secret"],
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
additionalProperties: false,
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
additionalProperties: false,
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
this.schemas.set("pipeline", pipelineSchema);
|
|
218
|
+
this.schemas.set("ai", aiConfigSchema);
|
|
219
|
+
this.schemas.set("security", securitySchema);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
validate(config, schemaName) {
|
|
223
|
+
const schema = this.schemas.get(schemaName);
|
|
224
|
+
if (!schema) {
|
|
225
|
+
throw new Error(`Unknown schema: ${schemaName}`);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const validate = this.ajv.compile(schema);
|
|
229
|
+
const valid = validate(config);
|
|
230
|
+
|
|
231
|
+
if (!valid) {
|
|
232
|
+
const errors = validate.errors.map((error) => ({
|
|
233
|
+
path: error.instancePath || error.schemaPath,
|
|
234
|
+
message: error.message,
|
|
235
|
+
value: error.data,
|
|
236
|
+
allowedValues: error.schema?.enum,
|
|
237
|
+
}));
|
|
238
|
+
|
|
239
|
+
logger.error("Configuration validation failed", {
|
|
240
|
+
schema: schemaName,
|
|
241
|
+
errors,
|
|
242
|
+
config: this._sanitizeConfig(config),
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
valid: false,
|
|
247
|
+
errors,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
logger.info("Configuration validation passed", {
|
|
252
|
+
schema: schemaName,
|
|
253
|
+
configKeys: Object.keys(config),
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
valid: true,
|
|
258
|
+
errors: [],
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
validatePipeline(config) {
|
|
263
|
+
return this.validate(config, "pipeline");
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
validateAI(config) {
|
|
267
|
+
return this.validate(config, "ai");
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
validateSecurity(config) {
|
|
271
|
+
return this.validate(config, "security");
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
addSchema(name, schema) {
|
|
275
|
+
this.schemas.set(name, schema);
|
|
276
|
+
logger.info("Custom schema registered", { schemaName: name });
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
getSchema(name) {
|
|
280
|
+
return this.schemas.get(name);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
listSchemas() {
|
|
284
|
+
return Array.from(this.schemas.keys());
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
_sanitizeConfig(config) {
|
|
288
|
+
// Remove sensitive fields from logs
|
|
289
|
+
const sensitiveFields = ["password", "token", "key", "secret", "apiKey"];
|
|
290
|
+
const sanitized = JSON.parse(JSON.stringify(config));
|
|
291
|
+
|
|
292
|
+
const sanitizeObject = (obj) => {
|
|
293
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
294
|
+
if (
|
|
295
|
+
sensitiveFields.some((field) => key.toLowerCase().includes(field))
|
|
296
|
+
) {
|
|
297
|
+
obj[key] = "[REDACTED]";
|
|
298
|
+
} else if (typeof value === "object" && value !== null) {
|
|
299
|
+
sanitizeObject(value);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
sanitizeObject(sanitized);
|
|
305
|
+
return sanitized;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Create singleton instance
|
|
310
|
+
const validator = new ConfigSchemaValidator();
|
|
311
|
+
|
|
312
|
+
module.exports = {
|
|
313
|
+
ConfigSchemaValidator,
|
|
314
|
+
validator,
|
|
315
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Version: 0.1.0
|
|
3
|
+
* Description: Schema validator for plugin._config.json
|
|
4
|
+
* Author: Ali Kahwaji
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
function validatePluginSchema(config) {
|
|
8
|
+
const validTypes = ["loader", "embedder", "retriever", "llm"];
|
|
9
|
+
const errors = [];
|
|
10
|
+
|
|
11
|
+
for (const _type of Object.keys(config)) {
|
|
12
|
+
if (!validTypes.includes(_type)) {
|
|
13
|
+
errors.push(`Unknown plugin _type: ${_type}`);
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
for (const [name, modulePath] of Object.entries(config[_type])) {
|
|
17
|
+
if (typeof name !== "string" || typeof modulePath !== "string") {
|
|
18
|
+
errors.push(
|
|
19
|
+
`Invalid plugin definition for _type "${_type}" and name "${name}"`,
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
valid: errors.length === 0,
|
|
27
|
+
errors,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Default export
|
|
32
|
+
|
|
33
|
+
module.exports = {
|
|
34
|
+
validatePluginSchema,
|
|
35
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Version: 1.3.0
|
|
3
|
+
* Description: AJV-powered validators for .ragrc.json full _config and plugin-only structure
|
|
4
|
+
* Author: Ali Kahwaji
|
|
5
|
+
* File: /src/_config/validate-schema.js
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const Ajv = require("ajv");
|
|
9
|
+
// eslint-disable-line global-require
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Full .ragrc.json schema (used in load-_config.js)
|
|
13
|
+
*/
|
|
14
|
+
const ragrcSchema = {
|
|
15
|
+
type: "object",
|
|
16
|
+
required: ["loader", "embedder", "retriever", "llm", "namespace", "pipeline"],
|
|
17
|
+
properties: {
|
|
18
|
+
loader: {
|
|
19
|
+
type: "object",
|
|
20
|
+
minProperties: 1,
|
|
21
|
+
additionalProperties: { type: "string" },
|
|
22
|
+
},
|
|
23
|
+
embedder: {
|
|
24
|
+
type: "object",
|
|
25
|
+
minProperties: 1,
|
|
26
|
+
additionalProperties: { type: "string" },
|
|
27
|
+
},
|
|
28
|
+
retriever: {
|
|
29
|
+
type: "object",
|
|
30
|
+
minProperties: 1,
|
|
31
|
+
additionalProperties: { type: "string" },
|
|
32
|
+
},
|
|
33
|
+
llm: {
|
|
34
|
+
type: "object",
|
|
35
|
+
minProperties: 1,
|
|
36
|
+
additionalProperties: { type: "string" },
|
|
37
|
+
},
|
|
38
|
+
namespace: {
|
|
39
|
+
type: "string",
|
|
40
|
+
minLength: 1,
|
|
41
|
+
},
|
|
42
|
+
pipeline: {
|
|
43
|
+
type: "array",
|
|
44
|
+
items: {
|
|
45
|
+
type: "string",
|
|
46
|
+
enum: ["loader", "embedder", "retriever"],
|
|
47
|
+
},
|
|
48
|
+
minItems: 1,
|
|
49
|
+
uniqueItems: true,
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
additionalProperties: false,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Minimal plugin-only schema (used in load-plugin-_config.js)
|
|
57
|
+
*/
|
|
58
|
+
const pluginSchema = {
|
|
59
|
+
type: "object",
|
|
60
|
+
required: ["loader", "embedder", "retriever", "llm"],
|
|
61
|
+
properties: {
|
|
62
|
+
loader: {
|
|
63
|
+
type: "object",
|
|
64
|
+
minProperties: 1,
|
|
65
|
+
additionalProperties: { type: "string" },
|
|
66
|
+
},
|
|
67
|
+
embedder: {
|
|
68
|
+
type: "object",
|
|
69
|
+
minProperties: 1,
|
|
70
|
+
additionalProperties: { type: "string" },
|
|
71
|
+
},
|
|
72
|
+
retriever: {
|
|
73
|
+
type: "object",
|
|
74
|
+
minProperties: 1,
|
|
75
|
+
additionalProperties: { type: "string" },
|
|
76
|
+
},
|
|
77
|
+
llm: {
|
|
78
|
+
type: "object",
|
|
79
|
+
minProperties: 1,
|
|
80
|
+
additionalProperties: { type: "string" },
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
additionalProperties: true, // allow namespace, pipeline, etc.
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Validates the full .ragrc.json _config structure
|
|
88
|
+
* @param {object} config
|
|
89
|
+
* @returns {{ valid: boolean, errors?: any[] }}
|
|
90
|
+
*/
|
|
91
|
+
function validateRagrcSchema(config) {
|
|
92
|
+
const ajv = new Ajv({ allErrors: true, strict: false });
|
|
93
|
+
const validate = ajv.compile(ragrcSchema);
|
|
94
|
+
const valid = validate(config);
|
|
95
|
+
return valid ? { valid: true } : { valid: false, errors: validate.errors };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Validates a plugin-only structure (for runtime plugin loading)
|
|
100
|
+
* @param {object} config
|
|
101
|
+
* @returns {{ valid: boolean, errors?: any[] }}
|
|
102
|
+
*/
|
|
103
|
+
function validatePluginSchema(config) {
|
|
104
|
+
const ajv = new Ajv({ allErrors: true, strict: false });
|
|
105
|
+
const validate = ajv.compile(pluginSchema);
|
|
106
|
+
const valid = validate(config);
|
|
107
|
+
return valid ? { valid: true } : { valid: false, errors: validate.errors };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
module.exports = {
|
|
111
|
+
validateRagrcSchema,
|
|
112
|
+
validatePluginSchema,
|
|
113
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin Contract Definitions
|
|
3
|
+
* Minimal fixture for test compatibility
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const pluginContracts = {
|
|
7
|
+
loader: {
|
|
8
|
+
name: 'loader',
|
|
9
|
+
version: '1.0.0',
|
|
10
|
+
methods: ['load'],
|
|
11
|
+
description: 'Document loader contract',
|
|
12
|
+
},
|
|
13
|
+
embedder: {
|
|
14
|
+
name: 'embedder',
|
|
15
|
+
version: '1.0.0',
|
|
16
|
+
methods: ['embed', 'embedQuery'],
|
|
17
|
+
description: 'Text embedder contract',
|
|
18
|
+
},
|
|
19
|
+
retriever: {
|
|
20
|
+
name: 'retriever',
|
|
21
|
+
version: '1.0.0',
|
|
22
|
+
methods: ['store', 'retrieve'],
|
|
23
|
+
description: 'Vector retriever contract',
|
|
24
|
+
},
|
|
25
|
+
llm: {
|
|
26
|
+
name: 'llm',
|
|
27
|
+
version: '1.0.0',
|
|
28
|
+
methods: ['generate'],
|
|
29
|
+
description: 'Language model contract',
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
module.exports = { pluginContracts };
|