@devilsdev/rag-pipeline-utils 2.1.12 → 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.
Files changed (110) hide show
  1. package/.ragrc.schema.json +218 -0
  2. package/README.md +292 -32
  3. package/bin/cli.js +8 -14
  4. package/dist/index.d.ts +237 -0
  5. package/dist/index.mjs +22 -7
  6. package/package.json +17 -13
  7. package/src/ai/adaptive-retrieval.js +191 -0
  8. package/src/ai/federated-learning.js +214 -0
  9. package/src/ai/federation/federated-learning-coordinator.js +345 -0
  10. package/src/ai/federation.js +76 -0
  11. package/src/ai/index-fixed.js +509 -0
  12. package/src/ai/index.js +33 -0
  13. package/src/ai/model-trainer.js +217 -0
  14. package/src/ai/model-training.js +205 -0
  15. package/src/ai/module-template.js +37 -0
  16. package/src/ai/multimodal/multi-modal-processor.js +345 -0
  17. package/src/ai/multimodal-processing.js +268 -0
  18. package/src/ai/multimodal.js +71 -0
  19. package/src/ai/orchestrator.js +70 -0
  20. package/src/ai/retrieval/adaptive-retrieval-engine.js +398 -0
  21. package/src/ai/retrieval-engine.js +69 -0
  22. package/src/ai/training/model-training-orchestrator.js +228 -0
  23. package/src/ci/workflow-analyzer.js +71 -0
  24. package/src/cli/commands/ai-ml.js +967 -0
  25. package/src/cli/commands/docs.js +228 -0
  26. package/src/cli/commands/dx.js +538 -0
  27. package/src/cli/commands/plugin-hub.js +875 -0
  28. package/src/cli/doctor-command.js +639 -0
  29. package/src/cli/enhanced-cli-commands.js +945 -0
  30. package/src/cli/interactive-wizard.js +997 -0
  31. package/src/cli/plugin-marketplace-commands.js +869 -0
  32. package/src/cli.js +17 -0
  33. package/src/config/enhanced-ragrc-schema.js +632 -0
  34. package/src/config/load-config.js +54 -0
  35. package/src/config/load-plugin-config.js +69 -0
  36. package/src/config/normalize-config.js +105 -0
  37. package/src/config/schema-validator.js +315 -0
  38. package/src/config/validate-plugin-schema.js +35 -0
  39. package/src/config/validate-schema.js +113 -0
  40. package/src/contracts/plugin-contracts.js +33 -0
  41. package/src/core/create-pipeline.js +99 -0
  42. package/src/core/dependency-injection.js +242 -0
  43. package/src/core/observability/event-logger.js +241 -0
  44. package/src/core/observability/instrumented-pipeline.js +609 -0
  45. package/src/core/observability/metrics.js +497 -0
  46. package/src/core/observability/tracing.js +781 -0
  47. package/src/core/performance/benchmark.js +516 -0
  48. package/src/core/performance/parallel-processor.js +240 -0
  49. package/src/core/performance/streaming-safeguards.js +611 -0
  50. package/src/core/pipeline-factory.js +60 -0
  51. package/src/core/plugin-contracts.js +89 -0
  52. package/src/core/plugin-marketplace/plugin-metadata.js +485 -0
  53. package/src/core/plugin-marketplace/plugin-publisher.js +574 -0
  54. package/src/core/plugin-marketplace/plugin-registry-format.js +419 -0
  55. package/src/core/plugin-marketplace/version-resolver.js +61 -0
  56. package/src/core/plugin-registry.js +265 -0
  57. package/src/dag/dag-engine.js +954 -0
  58. package/src/dag/dag-node.js +76 -0
  59. package/src/dag/enhanced-dag-engine.js +822 -0
  60. package/src/dx/index.js +40 -0
  61. package/src/dx/integration-templates.js +665 -0
  62. package/src/dx/performance-profiler.js +968 -0
  63. package/src/dx/realtime-debugger.js +755 -0
  64. package/src/dx/visual-pipeline-builder.js +578 -0
  65. package/src/ecosystem/plugin-analytics-dashboard.js +778 -0
  66. package/src/ecosystem/plugin-certification.js +580 -0
  67. package/src/ecosystem/plugin-hub.js +821 -0
  68. package/src/enterprise/audit-logging.js +967 -0
  69. package/src/enterprise/data-governance.js +408 -0
  70. package/src/enterprise/multi-tenancy.js +496 -0
  71. package/src/enterprise/sso-integration.js +903 -0
  72. package/src/evaluate/evaluator.js +52 -0
  73. package/src/evaluate/scoring.js +91 -0
  74. package/src/index.js +67 -0
  75. package/src/ingest.js +59 -0
  76. package/src/loader/csv-loader.js +38 -0
  77. package/src/loader/directory-loader.js +48 -0
  78. package/src/loader/html-loader.js +62 -0
  79. package/src/loader/markdown-loader.js +67 -0
  80. package/src/mocks/openai-embedder.js +119 -0
  81. package/src/mocks/openai-llm.js +77 -0
  82. package/src/mocks/pdf-loader.js +21 -0
  83. package/src/mocks/pinecone-retriever.js +106 -0
  84. package/src/observability/audit-logger.js +758 -0
  85. package/src/observability/dashboard-config.json +675 -0
  86. package/src/observability/event-logger.js +310 -0
  87. package/src/observability/metrics.js +691 -0
  88. package/src/observability/security-monitor.js +830 -0
  89. package/src/observability/slo-monitor.js +319 -0
  90. package/src/observability/telemetry.js +686 -0
  91. package/src/pipeline/pipeline.js +18 -0
  92. package/src/plugins/sample-embedder.js +53 -0
  93. package/src/plugins/sample-llm.js +30 -0
  94. package/src/plugins/sample-loader.js +24 -0
  95. package/src/plugins/sample-retriever.js +84 -0
  96. package/src/query.js +117 -0
  97. package/src/reranker/llm-reranker.js +85 -0
  98. package/src/scripts/script-utilities.js +36 -0
  99. package/src/security/plugin-sandbox.js +586 -0
  100. package/src/security/plugin-signature-verifier.js +414 -0
  101. package/src/security/validator.js +36 -0
  102. package/src/utils/ci/diagnostic-reporter.js +57 -0
  103. package/src/utils/logger.js +211 -0
  104. package/src/utils/performance-monitor.js +561 -0
  105. package/src/utils/plugin-scaffolder.js +447 -0
  106. package/src/utils/retry-policy.js +443 -0
  107. package/src/utils/retry.js +107 -0
  108. package/src/utils/safe-expression-evaluator.js +579 -0
  109. package/src/utils/structured-logger.js +361 -0
  110. package/src/utils/validate-plugin-contract.js +42 -0
@@ -0,0 +1,822 @@
1
+ /**
2
+ * @fileoverview Enhanced DAG Engine with Performance Optimizations
3
+ * Enterprise-grade DAG execution engine with advanced concurrency, retry policies,
4
+ * structured logging, and comprehensive error handling
5
+ *
6
+ * @author DevilsDev Team
7
+ * @since 2.1.8
8
+ * @version 2.1.8
9
+ */
10
+
11
+ const { logger } = require('../utils/structured-logger.js');
12
+ const {
13
+ defaultPolicy: defaultRetryPolicy,
14
+ } = require('../utils/retry-policy.js');
15
+
16
+ /**
17
+ * @typedef {Object} DAGNodeOptions
18
+ * @property {number} [timeout=30000] - Node execution timeout in milliseconds
19
+ * @property {number} [retries=0] - Number of retry attempts
20
+ * @property {number} [priority=0] - Execution priority (higher executes first)
21
+ * @property {boolean} [optional=false] - Whether node failure should be ignored
22
+ * @property {Object} [retryPolicy] - Custom retry policy configuration
23
+ * @property {string[]} [tags] - Node tags for categorization
24
+ * @property {Object} [metadata] - Additional node metadata
25
+ */
26
+
27
+ /**
28
+ * @typedef {Object} ExecutionContext
29
+ * @property {Map} results - Map of completed node results
30
+ * @property {Map} errors - Map of node execution errors
31
+ * @property {Map} fwd - Forward adjacency map for dependencies
32
+ * @property {boolean} continueOnError - Whether to continue on non-critical failures
33
+ * @property {Set} requiredIds - Set of required node IDs
34
+ * @property {any} seed - Initial seed value for source nodes
35
+ * @property {boolean} gracefulDegradation - Enable graceful degradation mode
36
+ * @property {string[]} requiredNodes - Array of required node IDs
37
+ * @property {boolean} retryFailedNodes - Enable retry for failed nodes
38
+ * @property {number} maxRetries - Maximum retry attempts per node
39
+ */
40
+
41
+ /**
42
+ * @typedef {Object} ExecutionOptions
43
+ * @property {number} [timeout=30000] - Global execution timeout in milliseconds
44
+ * @property {number} [concurrency=5] - Maximum concurrent node executions
45
+ * @property {boolean} [continueOnError=false] - Continue execution on non-critical failures
46
+ * @property {boolean} [gracefulDegradation=false] - Enable graceful degradation mode
47
+ * @property {string[]} [requiredNodes] - Array of required node IDs
48
+ * @property {boolean} [retryFailedNodes=false] - Enable retry for failed nodes
49
+ * @property {number} [maxRetries=3] - Maximum retry attempts per node
50
+ * @property {boolean} [enableMetrics=true] - Enable performance metrics collection
51
+ * @property {boolean} [enableTracing=false] - Enable execution tracing
52
+ */
53
+
54
+ /**
55
+ * @typedef {Object} NodeMetrics
56
+ * @property {number} executionTime - Node execution time in milliseconds
57
+ * @property {number} retryCount - Number of retry attempts made
58
+ * @property {string} status - Execution status: 'success' | 'failed' | 'skipped'
59
+ * @property {Date} startTime - Execution start timestamp
60
+ * @property {Date} endTime - Execution end timestamp
61
+ * @property {number} memoryUsage - Memory usage during execution
62
+ * @property {string[]} dependencies - List of dependency node IDs
63
+ */
64
+
65
+ /**
66
+ * Enhanced DAG Node with performance monitoring and retry capabilities
67
+ */
68
+ class EnhancedDAGNode {
69
+ /**
70
+ * Create a new DAG node
71
+ * @param {string} id - Unique node identifier
72
+ * @param {Function} run - Async function to execute: (input) => output
73
+ * @param {DAGNodeOptions} [options={}] - Node configuration options
74
+ */
75
+ constructor(id, run, options = {}) {
76
+ if (!id || typeof id !== 'string') {
77
+ throw new Error('Node ID must be a non-empty string');
78
+ }
79
+ if (!run || typeof run !== 'function') {
80
+ throw new Error('Node run function must be provided');
81
+ }
82
+
83
+ this.id = id;
84
+ this.run = run;
85
+ this.inputs = [];
86
+ this.outputs = [];
87
+
88
+ // Configuration options
89
+ this.timeout =
90
+ options.timeout || parseInt(process.env.RAG_NODE_TIMEOUT) || 30000;
91
+ this.retries = options.retries || 0;
92
+ this.priority = options.priority || 0;
93
+ this.optional = options.optional || false;
94
+ this.retryPolicy = options.retryPolicy || null;
95
+ this.tags = options.tags || [];
96
+ this.metadata = options.metadata || {};
97
+
98
+ // Performance tracking
99
+ this.metrics = {
100
+ executionCount: 0,
101
+ totalExecutionTime: 0,
102
+ successCount: 0,
103
+ failureCount: 0,
104
+ retryCount: 0,
105
+ lastExecutionTime: null,
106
+ avgExecutionTime: 0,
107
+ };
108
+
109
+ // Create child logger with node context
110
+ this.logger = logger.child({
111
+ component: 'dag-node',
112
+ nodeId: this.id,
113
+ priority: this.priority,
114
+ optional: this.optional,
115
+ });
116
+ }
117
+
118
+ /**
119
+ * Add an output connection to another node
120
+ * @param {EnhancedDAGNode} node - Target node to connect to
121
+ * @returns {EnhancedDAGNode} This node for chaining
122
+ */
123
+ addOutput(node) {
124
+ if (!node || !(node instanceof EnhancedDAGNode)) {
125
+ throw new Error(
126
+ 'Invalid output node: must be an EnhancedDAGNode instance',
127
+ );
128
+ }
129
+
130
+ if (!this.outputs.includes(node)) {
131
+ this.outputs.push(node);
132
+ this.logger.debug('Added output connection', { targetNodeId: node.id });
133
+ }
134
+ if (!node.inputs.includes(this)) {
135
+ node.inputs.push(this);
136
+ }
137
+ return this;
138
+ }
139
+
140
+ /**
141
+ * Add an input connection from another node
142
+ * @param {EnhancedDAGNode} node - Source node to connect from
143
+ * @returns {EnhancedDAGNode} This node for chaining
144
+ */
145
+ addInput(node) {
146
+ if (!node || !(node instanceof EnhancedDAGNode)) {
147
+ throw new Error(
148
+ 'Invalid input node: must be an EnhancedDAGNode instance',
149
+ );
150
+ }
151
+
152
+ if (!this.inputs.includes(node)) {
153
+ this.inputs.push(node);
154
+ this.logger.debug('Added input connection', { sourceNodeId: node.id });
155
+ }
156
+ if (!node.outputs.includes(this)) {
157
+ node.outputs.push(this);
158
+ }
159
+ return this;
160
+ }
161
+
162
+ /**
163
+ * Get node dependencies as array of IDs
164
+ * @returns {string[]} Array of dependency node IDs
165
+ */
166
+ getDependencies() {
167
+ return this.inputs.map((node) => node.id);
168
+ }
169
+
170
+ /**
171
+ * Check if node has specific tag
172
+ * @param {string} tag - Tag to check for
173
+ * @returns {boolean} Whether node has the tag
174
+ */
175
+ hasTag(tag) {
176
+ return this.tags.includes(tag);
177
+ }
178
+
179
+ /**
180
+ * Update node metrics after execution
181
+ * @param {number} executionTime - Execution time in milliseconds
182
+ * @param {boolean} success - Whether execution was successful
183
+ * @param {number} retryCount - Number of retries made
184
+ */
185
+ updateMetrics(executionTime, success, retryCount = 0) {
186
+ this.metrics.executionCount++;
187
+ this.metrics.totalExecutionTime += executionTime;
188
+ this.metrics.retryCount += retryCount;
189
+ this.metrics.lastExecutionTime = executionTime;
190
+
191
+ if (success) {
192
+ this.metrics.successCount++;
193
+ } else {
194
+ this.metrics.failureCount++;
195
+ }
196
+
197
+ this.metrics.avgExecutionTime =
198
+ this.metrics.totalExecutionTime / this.metrics.executionCount;
199
+ }
200
+
201
+ /**
202
+ * Get node performance metrics
203
+ * @returns {NodeMetrics} Node performance metrics
204
+ */
205
+ getMetrics() {
206
+ return {
207
+ ...this.metrics,
208
+ dependencies: this.getDependencies(),
209
+ successRate:
210
+ this.metrics.executionCount > 0
211
+ ? this.metrics.successCount / this.metrics.executionCount
212
+ : 0,
213
+ };
214
+ }
215
+
216
+ /**
217
+ * Reset node metrics
218
+ */
219
+ resetMetrics() {
220
+ this.metrics = {
221
+ executionCount: 0,
222
+ totalExecutionTime: 0,
223
+ successCount: 0,
224
+ failureCount: 0,
225
+ retryCount: 0,
226
+ lastExecutionTime: null,
227
+ avgExecutionTime: 0,
228
+ };
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Enhanced DAG Engine with advanced performance and reliability features
234
+ */
235
+ class EnhancedDAGEngine {
236
+ /**
237
+ * Create a new enhanced DAG engine
238
+ * @param {Object} [options={}] - Engine configuration options
239
+ */
240
+ constructor(options = {}) {
241
+ this.nodes = new Map();
242
+ this.executionHistory = [];
243
+ this.enableMetrics = options.enableMetrics !== false;
244
+ this.enableTracing = options.enableTracing || false;
245
+
246
+ // Default execution options
247
+ this.defaultOptions = {
248
+ timeout: 30000,
249
+ concurrency: parseInt(process.env.RAG_MAX_CONCURRENCY) || 5,
250
+ continueOnError: false,
251
+ gracefulDegradation: false,
252
+ retryFailedNodes: false,
253
+ maxRetries: 3,
254
+ enableMetrics: this.enableMetrics,
255
+ enableTracing: this.enableTracing,
256
+ };
257
+
258
+ // Performance tracking
259
+ this.engineMetrics = {
260
+ totalExecutions: 0,
261
+ successfulExecutions: 0,
262
+ failedExecutions: 0,
263
+ avgExecutionTime: 0,
264
+ totalExecutionTime: 0,
265
+ };
266
+
267
+ // Create engine logger
268
+ this.logger = logger.child({
269
+ component: 'enhanced-dag-engine',
270
+ version: '2.1.8',
271
+ });
272
+
273
+ this.logger.info('Enhanced DAG Engine initialized', {
274
+ defaultConcurrency: this.defaultOptions.concurrency,
275
+ metricsEnabled: this.enableMetrics,
276
+ tracingEnabled: this.enableTracing,
277
+ });
278
+ }
279
+
280
+ /**
281
+ * Add a node to the DAG
282
+ * @param {string} id - Unique node identifier
283
+ * @param {Function} run - Async function to execute
284
+ * @param {DAGNodeOptions} [options={}] - Node configuration
285
+ * @returns {EnhancedDAGNode} The created node
286
+ */
287
+ addNode(id, run, options = {}) {
288
+ if (this.nodes.has(id)) {
289
+ throw new Error(`Node with ID '${id}' already exists`);
290
+ }
291
+
292
+ const node = new EnhancedDAGNode(id, run, options);
293
+ this.nodes.set(id, node);
294
+
295
+ this.logger.debug('Node added to DAG', {
296
+ nodeId: id,
297
+ priority: node.priority,
298
+ optional: node.optional,
299
+ timeout: node.timeout,
300
+ });
301
+
302
+ return node;
303
+ }
304
+
305
+ /**
306
+ * Get a node by ID
307
+ * @param {string} id - Node identifier
308
+ * @returns {EnhancedDAGNode|undefined} The node or undefined if not found
309
+ */
310
+ getNode(id) {
311
+ return this.nodes.get(id);
312
+ }
313
+
314
+ /**
315
+ * Connect two nodes
316
+ * @param {string} fromId - Source node ID
317
+ * @param {string} toId - Target node ID
318
+ * @returns {EnhancedDAGEngine} This engine for chaining
319
+ */
320
+ connect(fromId, toId) {
321
+ const fromNode = this.nodes.get(fromId);
322
+ const toNode = this.nodes.get(toId);
323
+
324
+ if (!fromNode) {
325
+ throw new Error(`Source node '${fromId}' not found`);
326
+ }
327
+ if (!toNode) {
328
+ throw new Error(`Target node '${toId}' not found`);
329
+ }
330
+
331
+ fromNode.addOutput(toNode);
332
+
333
+ this.logger.debug('Nodes connected', {
334
+ fromNodeId: fromId,
335
+ toNodeId: toId,
336
+ });
337
+
338
+ return this;
339
+ }
340
+
341
+ /**
342
+ * Validate DAG for cycles and structural integrity
343
+ * @throws {Error} If DAG is invalid
344
+ */
345
+ validate() {
346
+ this.logger.debug('Validating DAG structure');
347
+
348
+ // Check for cycles using DFS
349
+ const visited = new Set();
350
+ const recursionStack = new Set();
351
+
352
+ const hasCycle = (nodeId) => {
353
+ if (recursionStack.has(nodeId)) {
354
+ return true;
355
+ }
356
+ if (visited.has(nodeId)) {
357
+ return false;
358
+ }
359
+
360
+ visited.add(nodeId);
361
+ recursionStack.add(nodeId);
362
+
363
+ const node = this.nodes.get(nodeId);
364
+ for (const output of node.outputs) {
365
+ if (hasCycle(output.id)) {
366
+ return true;
367
+ }
368
+ }
369
+
370
+ recursionStack.delete(nodeId);
371
+ return false;
372
+ };
373
+
374
+ for (const nodeId of this.nodes.keys()) {
375
+ if (hasCycle(nodeId)) {
376
+ throw new Error(`Cycle detected in DAG involving node: ${nodeId}`);
377
+ }
378
+ }
379
+
380
+ // Validate node integrity
381
+ for (const [nodeId, node] of this.nodes) {
382
+ // Check that all input/output connections are bidirectional
383
+ for (const input of node.inputs) {
384
+ if (!input.outputs.includes(node)) {
385
+ throw new Error(
386
+ `Broken bidirectional connection: ${input.id} -> ${nodeId}`,
387
+ );
388
+ }
389
+ }
390
+ for (const output of node.outputs) {
391
+ if (!output.inputs.includes(node)) {
392
+ throw new Error(
393
+ `Broken bidirectional connection: ${nodeId} -> ${output.id}`,
394
+ );
395
+ }
396
+ }
397
+ }
398
+
399
+ this.logger.info('DAG validation completed successfully', {
400
+ nodeCount: this.nodes.size,
401
+ });
402
+ }
403
+
404
+ /**
405
+ * Get topological order of nodes for execution
406
+ * @returns {EnhancedDAGNode[]} Nodes in execution order
407
+ */
408
+ getTopologicalOrder() {
409
+ const visited = new Set();
410
+ const order = [];
411
+
412
+ const visit = (node) => {
413
+ if (visited.has(node.id)) {
414
+ return;
415
+ }
416
+
417
+ visited.add(node.id);
418
+
419
+ // Visit dependencies first (sorted by priority)
420
+ const sortedInputs = [...node.inputs].sort(
421
+ (a, b) => b.priority - a.priority,
422
+ );
423
+ for (const input of sortedInputs) {
424
+ visit(input);
425
+ }
426
+
427
+ order.push(node);
428
+ };
429
+
430
+ // Start with nodes that have no inputs, sorted by priority
431
+ const sourceNodes = Array.from(this.nodes.values())
432
+ .filter((node) => node.inputs.length === 0)
433
+ .sort((a, b) => b.priority - a.priority);
434
+
435
+ for (const node of sourceNodes) {
436
+ visit(node);
437
+ }
438
+
439
+ // Ensure all nodes are included
440
+ for (const node of this.nodes.values()) {
441
+ visit(node);
442
+ }
443
+
444
+ this.logger.debug('Topological order computed', {
445
+ nodeCount: order.length,
446
+ sourceNodes: sourceNodes.map((n) => n.id),
447
+ });
448
+
449
+ return order;
450
+ }
451
+
452
+ /**
453
+ * Execute a single node with comprehensive error handling and retry logic
454
+ * @param {EnhancedDAGNode} node - Node to execute
455
+ * @param {ExecutionContext} context - Execution context
456
+ * @returns {Promise<any>} Node execution result
457
+ */
458
+ async executeNode(node, context) {
459
+ const { results, errors, seed, maxRetries } = context;
460
+ const startTime = Date.now();
461
+ let retryCount = 0;
462
+
463
+ const correlationId = this.logger.getContext()?.correlationId;
464
+
465
+ node.logger.debug('Starting node execution', {
466
+ correlationId,
467
+ dependencies: node.getDependencies(),
468
+ hasResults: node.inputs.map((dep) => results.has(dep.id)),
469
+ });
470
+
471
+ try {
472
+ // Prepare input data from dependencies
473
+ let input = seed;
474
+ if (node.inputs.length > 0) {
475
+ input = {};
476
+ for (const dep of node.inputs) {
477
+ if (results.has(dep.id)) {
478
+ input[dep.id] = results.get(dep.id);
479
+ }
480
+ }
481
+ }
482
+
483
+ // Create execution function with timeout
484
+ const executeWithTimeout = async () => {
485
+ const timeoutPromise = new Promise((_, reject) => {
486
+ setTimeout(() => {
487
+ reject(
488
+ new Error(
489
+ `Node ${node.id} execution timeout after ${node.timeout}ms`,
490
+ ),
491
+ );
492
+ }, node.timeout);
493
+ });
494
+
495
+ return Promise.race([node.run(input), timeoutPromise]);
496
+ };
497
+
498
+ // Use retry policy if configured
499
+ let result;
500
+ if (node.retryPolicy || (node.retries > 0 && maxRetries > 0)) {
501
+ const retryPolicy = node.retryPolicy || defaultRetryPolicy;
502
+
503
+ result = await retryPolicy.execute(executeWithTimeout, {
504
+ nodeId: node.id,
505
+ correlationId,
506
+ });
507
+ retryCount = retryPolicy.getMetrics().retryBudget.currentRetries;
508
+ } else {
509
+ result = await executeWithTimeout();
510
+ }
511
+
512
+ const executionTime = Date.now() - startTime;
513
+
514
+ // Update metrics
515
+ if (this.enableMetrics) {
516
+ node.updateMetrics(executionTime, true, retryCount);
517
+ }
518
+
519
+ node.logger.info('Node execution completed successfully', {
520
+ correlationId,
521
+ executionTime,
522
+ retryCount,
523
+ resultType: typeof result,
524
+ });
525
+
526
+ return result;
527
+ } catch (error) {
528
+ const executionTime = Date.now() - startTime;
529
+
530
+ // Update metrics
531
+ if (this.enableMetrics) {
532
+ node.updateMetrics(executionTime, false, retryCount);
533
+ }
534
+
535
+ // Handle optional node failures
536
+ if (node.optional && context.gracefulDegradation) {
537
+ node.logger.warn(
538
+ 'Optional node failed, continuing with graceful degradation',
539
+ {
540
+ correlationId,
541
+ error: error.message,
542
+ executionTime,
543
+ retryCount,
544
+ },
545
+ );
546
+ return Symbol('failed-optional-node');
547
+ }
548
+
549
+ // Store error for required nodes
550
+ errors.set(node.id, error);
551
+
552
+ node.logger.error('Node execution failed', {
553
+ correlationId,
554
+ error: error.message,
555
+ errorCode: error.code,
556
+ executionTime,
557
+ retryCount,
558
+ stack: error.stack,
559
+ });
560
+
561
+ // Re-throw for required nodes
562
+ throw error;
563
+ }
564
+ }
565
+
566
+ /**
567
+ * Execute the DAG with advanced concurrency and error handling
568
+ * @param {any} [seed] - Initial seed value for source nodes
569
+ * @param {ExecutionOptions} [options={}] - Execution options
570
+ * @returns {Promise<Object>} Execution results
571
+ */
572
+ async execute(seed, options = {}) {
573
+ const executionId = require('crypto').randomUUID();
574
+ const startTime = Date.now();
575
+
576
+ // Merge options with defaults
577
+ const config = { ...this.defaultOptions, ...options };
578
+
579
+ return this.logger.withCorrelation(executionId, async () => {
580
+ this.logger.info('Starting DAG execution', {
581
+ executionId,
582
+ seedType: typeof seed,
583
+ nodeCount: this.nodes.size,
584
+ concurrency: config.concurrency,
585
+ timeout: config.timeout,
586
+ });
587
+
588
+ try {
589
+ // Validate DAG before execution
590
+ this.validate();
591
+
592
+ // Initialize execution state
593
+ const results = new Map();
594
+ const errors = new Map();
595
+ const order = this.getTopologicalOrder();
596
+
597
+ // Create execution context
598
+ const context = {
599
+ results,
600
+ errors,
601
+ seed,
602
+ continueOnError: config.continueOnError,
603
+ gracefulDegradation: config.gracefulDegradation,
604
+ retryFailedNodes: config.retryFailedNodes,
605
+ maxRetries: config.maxRetries,
606
+ };
607
+
608
+ // Execute nodes with concurrency control
609
+ const semaphore = new Semaphore(config.concurrency);
610
+ const nodePromises = new Map();
611
+
612
+ for (const node of order) {
613
+ const promise = semaphore.acquire().then(async (release) => {
614
+ try {
615
+ // Wait for dependencies to complete
616
+ const depPromises = node.inputs
617
+ .map((dep) => nodePromises.get(dep.id))
618
+ .filter(Boolean);
619
+
620
+ await Promise.all(depPromises);
621
+
622
+ // Execute the node
623
+ const result = await this.executeNode(node, context);
624
+
625
+ if (
626
+ typeof result !== 'symbol' ||
627
+ result.description !== 'failed-optional-node'
628
+ ) {
629
+ results.set(node.id, result);
630
+ }
631
+
632
+ return result;
633
+ } finally {
634
+ release();
635
+ }
636
+ });
637
+
638
+ nodePromises.set(node.id, promise);
639
+ }
640
+
641
+ // Wait for all nodes to complete
642
+ const allResults = await Promise.allSettled(
643
+ Array.from(nodePromises.values()),
644
+ );
645
+
646
+ const executionTime = Date.now() - startTime;
647
+
648
+ // Update engine metrics
649
+ if (this.enableMetrics) {
650
+ this.engineMetrics.totalExecutions++;
651
+ this.engineMetrics.totalExecutionTime += executionTime;
652
+ this.engineMetrics.avgExecutionTime =
653
+ this.engineMetrics.totalExecutionTime /
654
+ this.engineMetrics.totalExecutions;
655
+
656
+ if (errors.size === 0) {
657
+ this.engineMetrics.successfulExecutions++;
658
+ } else {
659
+ this.engineMetrics.failedExecutions++;
660
+ }
661
+ }
662
+
663
+ // Create execution summary
664
+ const summary = {
665
+ executionId,
666
+ success: errors.size === 0,
667
+ executionTime,
668
+ nodesExecuted: results.size,
669
+ totalNodes: this.nodes.size,
670
+ errors: Array.from(errors.entries()).map(([id, error]) => ({
671
+ nodeId: id,
672
+ message: error.message,
673
+ code: error.code,
674
+ })),
675
+ };
676
+
677
+ // Store execution history
678
+ if (this.enableTracing) {
679
+ this.executionHistory.push({
680
+ ...summary,
681
+ timestamp: new Date().toISOString(),
682
+ results: Object.fromEntries(results),
683
+ });
684
+
685
+ // Keep only last 100 executions
686
+ if (this.executionHistory.length > 100) {
687
+ this.executionHistory.shift();
688
+ }
689
+ }
690
+
691
+ this.logger.info('DAG execution completed', summary);
692
+
693
+ // Return results
694
+ return {
695
+ results: Object.fromEntries(results),
696
+ errors: Object.fromEntries(errors),
697
+ summary,
698
+ metrics: this.enableMetrics ? this.getMetrics() : null,
699
+ };
700
+ } catch (error) {
701
+ const executionTime = Date.now() - startTime;
702
+
703
+ if (this.enableMetrics) {
704
+ this.engineMetrics.totalExecutions++;
705
+ this.engineMetrics.failedExecutions++;
706
+ }
707
+
708
+ this.logger.error('DAG execution failed', {
709
+ executionId,
710
+ error: error.message,
711
+ executionTime,
712
+ stack: error.stack,
713
+ });
714
+
715
+ throw error;
716
+ }
717
+ });
718
+ }
719
+
720
+ /**
721
+ * Get comprehensive performance metrics
722
+ * @returns {Object} Performance metrics
723
+ */
724
+ getMetrics() {
725
+ const nodeMetrics = {};
726
+ for (const [id, node] of this.nodes) {
727
+ nodeMetrics[id] = node.getMetrics();
728
+ }
729
+
730
+ return {
731
+ engine: this.engineMetrics,
732
+ nodes: nodeMetrics,
733
+ summary: {
734
+ totalNodes: this.nodes.size,
735
+ avgSuccessRate:
736
+ Object.values(nodeMetrics).reduce(
737
+ (sum, metrics) => sum + metrics.successRate,
738
+ 0,
739
+ ) / this.nodes.size,
740
+ },
741
+ };
742
+ }
743
+
744
+ /**
745
+ * Reset all metrics
746
+ */
747
+ resetMetrics() {
748
+ this.engineMetrics = {
749
+ totalExecutions: 0,
750
+ successfulExecutions: 0,
751
+ failedExecutions: 0,
752
+ avgExecutionTime: 0,
753
+ totalExecutionTime: 0,
754
+ };
755
+
756
+ for (const node of this.nodes.values()) {
757
+ node.resetMetrics();
758
+ }
759
+
760
+ this.executionHistory = [];
761
+ }
762
+
763
+ /**
764
+ * Get execution history (if tracing is enabled)
765
+ * @returns {Array} Execution history
766
+ */
767
+ getExecutionHistory() {
768
+ return [...this.executionHistory];
769
+ }
770
+ }
771
+
772
+ /**
773
+ * Semaphore for concurrency control
774
+ */
775
+ class Semaphore {
776
+ constructor(maxConcurrency) {
777
+ this.maxConcurrency = maxConcurrency;
778
+ this.currentCount = 0;
779
+ this.waitQueue = [];
780
+ }
781
+
782
+ async acquire() {
783
+ return new Promise((resolve) => {
784
+ if (this.currentCount < this.maxConcurrency) {
785
+ this.currentCount++;
786
+ resolve(() => this.release());
787
+ } else {
788
+ this.waitQueue.push(() => {
789
+ this.currentCount++;
790
+ resolve(() => this.release());
791
+ });
792
+ }
793
+ });
794
+ }
795
+
796
+ release() {
797
+ this.currentCount--;
798
+ if (this.waitQueue.length > 0) {
799
+ const next = this.waitQueue.shift();
800
+ next();
801
+ }
802
+ }
803
+ }
804
+
805
+ /**
806
+ * Create enhanced DAG engine instance
807
+ * @param {Object} [options={}] - Engine configuration
808
+ * @returns {EnhancedDAGEngine} DAG engine instance
809
+ */
810
+ function createEnhancedDAG(options = {}) {
811
+ return new EnhancedDAGEngine(options);
812
+ }
813
+
814
+ module.exports = {
815
+ EnhancedDAGEngine,
816
+ EnhancedDAGNode,
817
+ Semaphore,
818
+ createEnhancedDAG,
819
+ };
820
+
821
+ // CommonJS/ESM interop
822
+ module.exports.default = module.exports;