@devilsdev/rag-pipeline-utils 2.1.11 → 2.2.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 +218 -0
- package/README.md +651 -393
- package/bin/cli.js +8 -14
- package/dist/index.d.ts +237 -0
- package/dist/index.mjs +22 -7
- package/package.json +17 -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 +611 -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 +265 -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 +903 -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 +85 -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/retry-policy.js +443 -0
- package/src/utils/retry.js +107 -0
- package/src/utils/safe-expression-evaluator.js +579 -0
- package/src/utils/structured-logger.js +361 -0
- package/src/utils/validate-plugin-contract.js +42 -0
|
@@ -0,0 +1,578 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Visual Pipeline Builder
|
|
3
|
+
*
|
|
4
|
+
* Web-based drag-and-drop interface for creating and configuring RAG pipelines.
|
|
5
|
+
* Provides visual representation of pipeline components and their connections.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const EventEmitter = require("events");
|
|
9
|
+
// eslint-disable-line global-require
|
|
10
|
+
const fs = require("fs").promises;
|
|
11
|
+
// eslint-disable-line global-require
|
|
12
|
+
const path = require("path");
|
|
13
|
+
// eslint-disable-line global-require
|
|
14
|
+
|
|
15
|
+
class VisualPipelineBuilder extends EventEmitter {
|
|
16
|
+
constructor(_options = {}) {
|
|
17
|
+
super();
|
|
18
|
+
|
|
19
|
+
this._options = {
|
|
20
|
+
port: _options.port || 3001,
|
|
21
|
+
host: _options.host || "localhost",
|
|
22
|
+
autoSave: _options.autoSave !== false,
|
|
23
|
+
theme: _options.theme || "light",
|
|
24
|
+
..._options,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
this.pipelines = new Map();
|
|
28
|
+
this.components = new Map();
|
|
29
|
+
this.connections = new Map();
|
|
30
|
+
this.isRunning = false;
|
|
31
|
+
|
|
32
|
+
this.initializeComponents();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Initialize available pipeline components
|
|
37
|
+
*/
|
|
38
|
+
initializeComponents() {
|
|
39
|
+
const defaultComponents = [
|
|
40
|
+
{
|
|
41
|
+
id: "loader",
|
|
42
|
+
type: "input",
|
|
43
|
+
name: "Document Loader",
|
|
44
|
+
description: "Load documents from various sources",
|
|
45
|
+
inputs: [],
|
|
46
|
+
outputs: ["documents"],
|
|
47
|
+
config: {
|
|
48
|
+
source: { type: "string", required: true },
|
|
49
|
+
format: { type: "select", options: ["json", "txt", "pdf", "docx"] },
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: "embedder",
|
|
54
|
+
type: "processor",
|
|
55
|
+
name: "Text Embedder",
|
|
56
|
+
description: "Generate embeddings for text content",
|
|
57
|
+
inputs: ["documents"],
|
|
58
|
+
outputs: ["embeddings"],
|
|
59
|
+
config: {
|
|
60
|
+
model: { type: "string", default: "openai" },
|
|
61
|
+
dimensions: { type: "number", default: 1536 },
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: "retriever",
|
|
66
|
+
type: "processor",
|
|
67
|
+
name: "Document Retriever",
|
|
68
|
+
description: "Retrieve relevant documents based on query",
|
|
69
|
+
inputs: ["embeddings", "query"],
|
|
70
|
+
outputs: ["results"],
|
|
71
|
+
config: {
|
|
72
|
+
topK: { type: "number", default: 5 },
|
|
73
|
+
threshold: { type: "number", default: 0.7 },
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: "llm",
|
|
78
|
+
type: "processor",
|
|
79
|
+
name: "Language Model",
|
|
80
|
+
description: "Generate responses using LLM",
|
|
81
|
+
inputs: ["results", "query"],
|
|
82
|
+
outputs: ["response"],
|
|
83
|
+
config: {
|
|
84
|
+
model: { type: "string", default: "gpt-3.5-turbo" },
|
|
85
|
+
temperature: { type: "number", default: 0.7 },
|
|
86
|
+
maxTokens: { type: "number", default: 1000 },
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: "reranker",
|
|
91
|
+
type: "processor",
|
|
92
|
+
name: "Result Reranker",
|
|
93
|
+
description: "Rerank search results for better relevance",
|
|
94
|
+
inputs: ["results"],
|
|
95
|
+
outputs: ["rankedResults"],
|
|
96
|
+
config: {
|
|
97
|
+
model: { type: "string", default: "cross-encoder" },
|
|
98
|
+
topK: { type: "number", default: 3 },
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: "evaluator",
|
|
103
|
+
type: "output",
|
|
104
|
+
name: "Response Evaluator",
|
|
105
|
+
description: "Evaluate response quality and metrics",
|
|
106
|
+
inputs: ["response", "query"],
|
|
107
|
+
outputs: ["metrics"],
|
|
108
|
+
config: {
|
|
109
|
+
metrics: { type: "array", default: ["bleu", "rouge", "relevance"] },
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
defaultComponents.forEach((component) => {
|
|
115
|
+
this.components.set(component.id, component);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Create a new pipeline
|
|
121
|
+
*/
|
|
122
|
+
createPipeline(name, description = "") {
|
|
123
|
+
const pipelineId = `pipeline_${Date.now()}`;
|
|
124
|
+
const pipeline = {
|
|
125
|
+
id: pipelineId,
|
|
126
|
+
name,
|
|
127
|
+
description,
|
|
128
|
+
components: [],
|
|
129
|
+
connections: [],
|
|
130
|
+
config: {},
|
|
131
|
+
created: new Date().toISOString(),
|
|
132
|
+
modified: new Date().toISOString(),
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
this.pipelines.set(pipelineId, pipeline);
|
|
136
|
+
this.emit("pipelineCreated", { pipelineId, pipeline });
|
|
137
|
+
|
|
138
|
+
return pipelineId;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Add component to pipeline
|
|
143
|
+
*/
|
|
144
|
+
addComponent(
|
|
145
|
+
pipelineId,
|
|
146
|
+
componentType,
|
|
147
|
+
position = { x: 0, y: 0 },
|
|
148
|
+
config = {},
|
|
149
|
+
) {
|
|
150
|
+
const pipeline = this.pipelines.get(pipelineId);
|
|
151
|
+
if (!pipeline) {
|
|
152
|
+
throw new Error(`Pipeline ${pipelineId} not found`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const componentTemplate = this.components.get(componentType);
|
|
156
|
+
if (!componentTemplate) {
|
|
157
|
+
throw new Error(`Component type ${componentType} not found`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const componentId = `${componentType}_${Date.now()}`;
|
|
161
|
+
const component = {
|
|
162
|
+
id: componentId,
|
|
163
|
+
type: componentType,
|
|
164
|
+
position,
|
|
165
|
+
config: { ...componentTemplate.config, ...config },
|
|
166
|
+
inputs: [...componentTemplate.inputs],
|
|
167
|
+
outputs: [...componentTemplate.outputs],
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
pipeline.components.push(component);
|
|
171
|
+
pipeline.modified = new Date().toISOString();
|
|
172
|
+
|
|
173
|
+
this.emit("componentAdded", { pipelineId, componentId, component });
|
|
174
|
+
|
|
175
|
+
if (this._options.autoSave) {
|
|
176
|
+
this.savePipeline(pipelineId);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return componentId;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Connect two components
|
|
184
|
+
*/
|
|
185
|
+
connectComponents(pipelineId, sourceId, sourceOutput, targetId, targetInput) {
|
|
186
|
+
const pipeline = this.pipelines.get(pipelineId);
|
|
187
|
+
if (!pipeline) {
|
|
188
|
+
throw new Error(`Pipeline ${pipelineId} not found`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const sourceComponent = pipeline.components.find((c) => c.id === sourceId);
|
|
192
|
+
const targetComponent = pipeline.components.find((c) => c.id === targetId);
|
|
193
|
+
|
|
194
|
+
if (!sourceComponent || !targetComponent) {
|
|
195
|
+
throw new Error("Source or target component not found");
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (!sourceComponent.outputs.includes(sourceOutput)) {
|
|
199
|
+
throw new Error(`Source component does not have output: ${sourceOutput}`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (!targetComponent.inputs.includes(targetInput)) {
|
|
203
|
+
throw new Error(`Target component does not have input: ${targetInput}`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const connectionId = `conn_${Date.now()}`;
|
|
207
|
+
const connection = {
|
|
208
|
+
id: connectionId,
|
|
209
|
+
source: { componentId: sourceId, output: sourceOutput },
|
|
210
|
+
target: { componentId: targetId, input: targetInput },
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
pipeline.connections.push(connection);
|
|
214
|
+
pipeline.modified = new Date().toISOString();
|
|
215
|
+
|
|
216
|
+
this.emit("connectionCreated", { pipelineId, connectionId, connection });
|
|
217
|
+
|
|
218
|
+
if (this._options.autoSave) {
|
|
219
|
+
this.savePipeline(pipelineId);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return connectionId;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Validate pipeline configuration
|
|
227
|
+
*/
|
|
228
|
+
validatePipeline(pipelineId) {
|
|
229
|
+
const pipeline = this.pipelines.get(pipelineId);
|
|
230
|
+
if (!pipeline) {
|
|
231
|
+
throw new Error(`Pipeline ${pipelineId} not found`);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const errors = [];
|
|
235
|
+
const warnings = [];
|
|
236
|
+
|
|
237
|
+
// Check for disconnected components
|
|
238
|
+
const connectedComponents = new Set();
|
|
239
|
+
pipeline.connections.forEach((conn) => {
|
|
240
|
+
connectedComponents.add(conn.source.componentId);
|
|
241
|
+
connectedComponents.add(conn.target.componentId);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
pipeline.components.forEach((component) => {
|
|
245
|
+
if (
|
|
246
|
+
!connectedComponents.has(component.id) &&
|
|
247
|
+
pipeline.components.length > 1
|
|
248
|
+
) {
|
|
249
|
+
warnings.push(`Component ${component.id} is not connected`);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Check required configuration
|
|
253
|
+
const template = this.components.get(component.type);
|
|
254
|
+
if (template) {
|
|
255
|
+
Object.entries(template.config).forEach(([key, configDef]) => {
|
|
256
|
+
if (configDef.required && !component.config[key]) {
|
|
257
|
+
errors.push(
|
|
258
|
+
`Component ${component.id} missing required config: ${key}`,
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// Check for cycles
|
|
266
|
+
if (this.hasCycles(pipeline)) {
|
|
267
|
+
errors.push("Pipeline contains cycles");
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return {
|
|
271
|
+
valid: errors.length === 0,
|
|
272
|
+
errors,
|
|
273
|
+
warnings,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Check for cycles in pipeline
|
|
279
|
+
*/
|
|
280
|
+
hasCycles(pipeline) {
|
|
281
|
+
const graph = new Map();
|
|
282
|
+
const visited = new Set();
|
|
283
|
+
const recursionStack = new Set();
|
|
284
|
+
|
|
285
|
+
// Build adjacency list
|
|
286
|
+
pipeline.components.forEach((component) => {
|
|
287
|
+
graph.set(component.id, []);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
pipeline.connections.forEach((connection) => {
|
|
291
|
+
const sourceId = connection.source.componentId;
|
|
292
|
+
const targetId = connection.target.componentId;
|
|
293
|
+
if (graph.has(sourceId)) {
|
|
294
|
+
graph.get(sourceId).push(targetId);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// DFS cycle detection
|
|
299
|
+
const hasCycleDFS = (nodeId) => {
|
|
300
|
+
visited.add(nodeId);
|
|
301
|
+
recursionStack.add(nodeId);
|
|
302
|
+
|
|
303
|
+
const neighbors = graph.get(nodeId) || [];
|
|
304
|
+
for (const neighbor of neighbors) {
|
|
305
|
+
if (!visited.has(neighbor)) {
|
|
306
|
+
if (hasCycleDFS(neighbor)) {
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
} else if (recursionStack.has(neighbor)) {
|
|
310
|
+
return true;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
recursionStack.delete(nodeId);
|
|
315
|
+
return false;
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
for (const componentId of graph.keys()) {
|
|
319
|
+
if (!visited.has(componentId)) {
|
|
320
|
+
if (hasCycleDFS(componentId)) {
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Generate pipeline code
|
|
331
|
+
*/
|
|
332
|
+
generateCode(pipelineId, format = "javascript") {
|
|
333
|
+
const pipeline = this.pipelines.get(pipelineId);
|
|
334
|
+
if (!pipeline) {
|
|
335
|
+
throw new Error(`Pipeline ${pipelineId} not found`);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const validation = this.validatePipeline(pipelineId);
|
|
339
|
+
if (!validation.valid) {
|
|
340
|
+
throw new Error(
|
|
341
|
+
`Pipeline validation failed: ${validation.errors.join(", ")}`,
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (format === "javascript") {
|
|
346
|
+
return this.generateJavaScriptCode(pipeline);
|
|
347
|
+
} else if (format === "json") {
|
|
348
|
+
return this.generateJSONConfig(pipeline);
|
|
349
|
+
} else {
|
|
350
|
+
throw new Error(`Unsupported format: ${format}`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Generate JavaScript code for pipeline
|
|
356
|
+
*/
|
|
357
|
+
generateJavaScriptCode(pipeline) {
|
|
358
|
+
const imports = new Set();
|
|
359
|
+
const componentInits = [];
|
|
360
|
+
const pipelineSteps = [];
|
|
361
|
+
|
|
362
|
+
// Sort components topologically
|
|
363
|
+
const sortedComponents = this.topologicalSort(pipeline);
|
|
364
|
+
|
|
365
|
+
sortedComponents.forEach((component) => {
|
|
366
|
+
const template = this.components.get(component.type);
|
|
367
|
+
if (template) {
|
|
368
|
+
imports.add(
|
|
369
|
+
`const ${template.name.replace(/\s+/g, "")} = require('@devilsdev/rag-pipeline-utils/src/plugins/${component.type}');`,
|
|
370
|
+
);
|
|
371
|
+
// eslint-disable-line global-require
|
|
372
|
+
|
|
373
|
+
const configStr = JSON.stringify(component.config, null, 2);
|
|
374
|
+
componentInits.push(
|
|
375
|
+
`const ${component.id} = new ${template.name.replace(/\s+/g, "")}(${configStr});`,
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
pipelineSteps.push(` await ${component.id}.process(context);`);
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
return `// Generated RAG Pipeline: ${pipeline.name}
|
|
383
|
+
// Created: ${pipeline.created}
|
|
384
|
+
// Modified: ${pipeline.modified}
|
|
385
|
+
|
|
386
|
+
${Array.from(imports).join("\n")}
|
|
387
|
+
|
|
388
|
+
async function ${pipeline.name.replace(/\s+/g, "")}Pipeline(input) {
|
|
389
|
+
const context = { input, results: {} };
|
|
390
|
+
|
|
391
|
+
// Initialize components
|
|
392
|
+
${componentInits.map((init) => ` ${init}`).join("\n")}
|
|
393
|
+
|
|
394
|
+
// Execute pipeline
|
|
395
|
+
${pipelineSteps.join("\n")}
|
|
396
|
+
|
|
397
|
+
return context.results;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
module.exports = { ${pipeline.name.replace(/\s+/g, "")}Pipeline };`;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Generate JSON configuration for pipeline
|
|
405
|
+
*/
|
|
406
|
+
generateJSONConfig(pipeline) {
|
|
407
|
+
return JSON.stringify(
|
|
408
|
+
{
|
|
409
|
+
name: pipeline.name,
|
|
410
|
+
description: pipeline.description,
|
|
411
|
+
version: "1.0.0",
|
|
412
|
+
components: pipeline.components.map((component) => ({
|
|
413
|
+
id: component.id,
|
|
414
|
+
type: component.type,
|
|
415
|
+
config: component.config,
|
|
416
|
+
})),
|
|
417
|
+
connections: pipeline.connections,
|
|
418
|
+
metadata: {
|
|
419
|
+
created: pipeline.created,
|
|
420
|
+
modified: pipeline.modified,
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
null,
|
|
424
|
+
2,
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Topological sort of pipeline components
|
|
430
|
+
*/
|
|
431
|
+
topologicalSort(pipeline) {
|
|
432
|
+
const graph = new Map();
|
|
433
|
+
const inDegree = new Map();
|
|
434
|
+
|
|
435
|
+
// Initialize
|
|
436
|
+
pipeline.components.forEach((component) => {
|
|
437
|
+
graph.set(component.id, []);
|
|
438
|
+
inDegree.set(component.id, 0);
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
// Build graph
|
|
442
|
+
pipeline.connections.forEach((connection) => {
|
|
443
|
+
const sourceId = connection.source.componentId;
|
|
444
|
+
const targetId = connection.target.componentId;
|
|
445
|
+
graph.get(sourceId).push(targetId);
|
|
446
|
+
inDegree.set(targetId, inDegree.get(targetId) + 1);
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
// Kahn's algorithm
|
|
450
|
+
const queue = [];
|
|
451
|
+
const result = [];
|
|
452
|
+
|
|
453
|
+
inDegree.forEach((degree, componentId) => {
|
|
454
|
+
if (degree === 0) {
|
|
455
|
+
queue.push(componentId);
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
while (queue.length > 0) {
|
|
460
|
+
const current = queue.shift();
|
|
461
|
+
const component = pipeline.components.find((c) => c.id === current);
|
|
462
|
+
result.push(component);
|
|
463
|
+
|
|
464
|
+
graph.get(current).forEach((neighbor) => {
|
|
465
|
+
inDegree.set(neighbor, inDegree.get(neighbor) - 1);
|
|
466
|
+
if (inDegree.get(neighbor) === 0) {
|
|
467
|
+
queue.push(neighbor);
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
return result;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Save pipeline to disk
|
|
477
|
+
*/
|
|
478
|
+
async savePipeline(pipelineId) {
|
|
479
|
+
const pipeline = this.pipelines.get(pipelineId);
|
|
480
|
+
if (!pipeline) {
|
|
481
|
+
throw new Error(`Pipeline ${pipelineId} not found`);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const pipelinesDir = path.join(process.cwd(), "pipelines");
|
|
485
|
+
await fs.mkdir(pipelinesDir, { recursive: true });
|
|
486
|
+
|
|
487
|
+
const _filePath = path.join(pipelinesDir, `${pipelineId}.json`);
|
|
488
|
+
await fs.writeFile(_filePath, JSON.stringify(pipeline, null, 2));
|
|
489
|
+
|
|
490
|
+
this.emit("pipelineSaved", { pipelineId, _filePath });
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Load pipeline from disk
|
|
495
|
+
*/
|
|
496
|
+
async loadPipeline(pipelineId) {
|
|
497
|
+
const pipelinesDir = path.join(process.cwd(), "pipelines");
|
|
498
|
+
const _filePath = path.join(pipelinesDir, `${pipelineId}.json`);
|
|
499
|
+
|
|
500
|
+
try {
|
|
501
|
+
const data = await fs.readFile(_filePath, "utf8");
|
|
502
|
+
const pipeline = JSON.parse(data);
|
|
503
|
+
this.pipelines.set(pipelineId, pipeline);
|
|
504
|
+
|
|
505
|
+
this.emit("pipelineLoaded", { pipelineId, pipeline });
|
|
506
|
+
return pipeline;
|
|
507
|
+
} catch (error) {
|
|
508
|
+
throw new Error(
|
|
509
|
+
`Failed to load pipeline ${pipelineId}: ${error.message}`,
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Get all pipelines
|
|
516
|
+
*/
|
|
517
|
+
getAllPipelines() {
|
|
518
|
+
return Array.from(this.pipelines.values());
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Get available components
|
|
523
|
+
*/
|
|
524
|
+
getAvailableComponents() {
|
|
525
|
+
return Array.from(this.components.values());
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Start the visual builder web server
|
|
530
|
+
*/
|
|
531
|
+
async startServer() {
|
|
532
|
+
if (this.isRunning) {
|
|
533
|
+
throw new Error("Server is already running");
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// This would typically start an Express server with the web interface
|
|
537
|
+
// For now, we'll simulate the server startup
|
|
538
|
+
this.isRunning = true;
|
|
539
|
+
this.emit("serverStarted", {
|
|
540
|
+
host: this._options.host,
|
|
541
|
+
port: this._options.port,
|
|
542
|
+
url: `http://${this.options.host}:${this.options.port}`,
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
return {
|
|
546
|
+
host: this._options.host,
|
|
547
|
+
port: this._options.port,
|
|
548
|
+
url: `http://${this.options.host}:${this.options.port}`,
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Stop the visual builder web server
|
|
554
|
+
*/
|
|
555
|
+
async stopServer() {
|
|
556
|
+
if (!this.isRunning) {
|
|
557
|
+
throw new Error("Server is not running");
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
this.isRunning = false;
|
|
561
|
+
this.emit("serverStopped");
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Get server status
|
|
566
|
+
*/
|
|
567
|
+
getServerStatus() {
|
|
568
|
+
return {
|
|
569
|
+
running: this.isRunning,
|
|
570
|
+
host: this._options.host,
|
|
571
|
+
port: this._options.port,
|
|
572
|
+
pipelines: this.pipelines.size,
|
|
573
|
+
components: this.components.size,
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
module.exports = VisualPipelineBuilder;
|