@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,691 @@
1
+ /**
2
+ * Observability Metrics
3
+ *
4
+ * Enterprise-grade metrics collection and monitoring for RAG pipelines.
5
+ * Provides Counter, Histogram, and PipelineMetrics classes for comprehensive observability.
6
+ */
7
+
8
+ const EventEmitter = require('events'); // eslint-disable-line global-require
9
+
10
+ /**
11
+ * Counter metric for tracking cumulative values
12
+ */
13
+ class Counter extends EventEmitter {
14
+ constructor(name, description = '', labels = []) {
15
+ super();
16
+
17
+ this.name = name;
18
+ this.description = description;
19
+ this.labels = labels;
20
+ this.value = 0;
21
+ this.labeledValues = new Map();
22
+ this.created = Date.now();
23
+ }
24
+
25
+ /**
26
+ * Increment counter by specified amount
27
+ */
28
+ inc(amount = 1, labelValues = {}) {
29
+ if (amount < 0) {
30
+ throw new Error(
31
+ 'Counter can only be incremented by non-negative amounts',
32
+ );
33
+ }
34
+
35
+ if (Object.keys(labelValues).length === 0) {
36
+ this.value += amount;
37
+ } else {
38
+ const labelKey = this._getLabelKey(labelValues);
39
+ const current = this.labeledValues.get(labelKey) || 0;
40
+ this.labeledValues.set(labelKey, current + amount);
41
+ }
42
+
43
+ this.emit('increment', { amount, labelValues, timestamp: Date.now() });
44
+ }
45
+
46
+ /**
47
+ * Get current counter value
48
+ */
49
+ get(labelValues = {}) {
50
+ if (Object.keys(labelValues).length === 0) {
51
+ return this.value;
52
+ }
53
+
54
+ const labelKey = this._getLabelKey(labelValues);
55
+ return this.labeledValues.get(labelKey) || 0;
56
+ }
57
+
58
+ /**
59
+ * Reset counter to zero
60
+ */
61
+ reset(labelValues = {}) {
62
+ if (Object.keys(labelValues).length === 0) {
63
+ this.value = 0;
64
+ } else {
65
+ const labelKey = this._getLabelKey(labelValues);
66
+ this.labeledValues.set(labelKey, 0);
67
+ }
68
+
69
+ this.emit('reset', { labelValues, timestamp: Date.now() });
70
+ }
71
+
72
+ /**
73
+ * Get all labeled values
74
+ */
75
+ getAllValues() {
76
+ const result = { _unlabeled: this.value };
77
+
78
+ for (const [labelKey, value] of this.labeledValues) {
79
+ result[labelKey] = value;
80
+ }
81
+
82
+ return result;
83
+ }
84
+
85
+ /**
86
+ * Generate label key from label values
87
+ */
88
+ _getLabelKey(labelValues) {
89
+ const sortedKeys = Object.keys(labelValues).sort();
90
+ return sortedKeys.map((key) => `${key}=${labelValues[key]}`).join(',');
91
+ }
92
+
93
+ /**
94
+ * Export metric data
95
+ */
96
+ export() {
97
+ return {
98
+ name: this.name,
99
+ description: this.description,
100
+ type: 'counter',
101
+ value: this.value,
102
+ labeledValues: Object.fromEntries(this.labeledValues),
103
+ created: this.created,
104
+ timestamp: Date.now(),
105
+ };
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Histogram metric for tracking distributions of values
111
+ */
112
+ class Histogram extends EventEmitter {
113
+ constructor(name, description = '', buckets = [0.1, 0.5, 1, 2.5, 5, 10]) {
114
+ super();
115
+
116
+ this.name = name;
117
+ this.description = description;
118
+ this.buckets = [...buckets].sort((a, b) => a - b);
119
+ this.bucketCounts = new Map();
120
+ this.sum = 0;
121
+ this.count = 0;
122
+ this.labeledMetrics = new Map();
123
+ this.created = Date.now();
124
+
125
+ // Initialize bucket counts
126
+ for (const bucket of this.buckets) {
127
+ this.bucketCounts.set(bucket, 0);
128
+ }
129
+ this.bucketCounts.set('+Inf', 0);
130
+ }
131
+
132
+ /**
133
+ * Observe a value
134
+ */
135
+ observe(value, labelValues = {}) {
136
+ if (typeof value !== 'number' || isNaN(value)) {
137
+ throw new Error('Histogram can only observe numeric values');
138
+ }
139
+
140
+ if (Object.keys(labelValues).length === 0) {
141
+ this._observeUnlabeled(value);
142
+ } else {
143
+ this._observeLabeled(value, labelValues);
144
+ }
145
+
146
+ this.emit('observe', { value, labelValues, timestamp: Date.now() });
147
+ }
148
+
149
+ /**
150
+ * Observe unlabeled value
151
+ */
152
+ _observeUnlabeled(value) {
153
+ this.sum += value;
154
+ this.count++;
155
+
156
+ // Update bucket counts
157
+ for (const bucket of this.buckets) {
158
+ if (value <= bucket) {
159
+ this.bucketCounts.set(bucket, this.bucketCounts.get(bucket) + 1);
160
+ }
161
+ }
162
+ this.bucketCounts.set('+Inf', this.bucketCounts.get('+Inf') + 1);
163
+ }
164
+
165
+ /**
166
+ * Observe labeled value
167
+ */
168
+ _observeLabeled(value, labelValues) {
169
+ const labelKey = this._getLabelKey(labelValues);
170
+
171
+ if (!this.labeledMetrics.has(labelKey)) {
172
+ this.labeledMetrics.set(labelKey, {
173
+ sum: 0,
174
+ count: 0,
175
+ bucketCounts: new Map(),
176
+ });
177
+
178
+ // Initialize buckets for this label combination
179
+ const metric = this.labeledMetrics.get(labelKey);
180
+ for (const bucket of this.buckets) {
181
+ metric.bucketCounts.set(bucket, 0);
182
+ }
183
+ metric.bucketCounts.set('+Inf', 0);
184
+ }
185
+
186
+ const metric = this.labeledMetrics.get(labelKey);
187
+ metric.sum += value;
188
+ metric.count++;
189
+
190
+ // Update bucket counts
191
+ for (const bucket of this.buckets) {
192
+ if (value <= bucket) {
193
+ metric.bucketCounts.set(bucket, metric.bucketCounts.get(bucket) + 1);
194
+ }
195
+ }
196
+ metric.bucketCounts.set('+Inf', metric.bucketCounts.get('+Inf') + 1);
197
+ }
198
+
199
+ /**
200
+ * Get histogram statistics
201
+ */
202
+ getStats(labelValues = {}) {
203
+ if (Object.keys(labelValues).length === 0) {
204
+ return {
205
+ count: this.count,
206
+ sum: this.sum,
207
+ average: this.count > 0 ? this.sum / this.count : 0,
208
+ buckets: Object.fromEntries(this.bucketCounts),
209
+ };
210
+ }
211
+
212
+ const labelKey = this._getLabelKey(labelValues);
213
+ const metric = this.labeledMetrics.get(labelKey);
214
+
215
+ if (!metric) {
216
+ return {
217
+ count: 0,
218
+ sum: 0,
219
+ average: 0,
220
+ buckets: {},
221
+ };
222
+ }
223
+
224
+ return {
225
+ count: metric.count,
226
+ sum: metric.sum,
227
+ average: metric.count > 0 ? metric.sum / metric.count : 0,
228
+ buckets: Object.fromEntries(metric.bucketCounts),
229
+ };
230
+ }
231
+
232
+ /**
233
+ * Calculate percentiles
234
+ */
235
+ getPercentiles(percentiles = [50, 90, 95, 99], labelValues = {}) {
236
+ const stats = this.getStats(labelValues);
237
+ const result = {};
238
+
239
+ if (stats.count === 0) {
240
+ for (const p of percentiles) {
241
+ result[`p${p}`] = 0;
242
+ }
243
+ return result;
244
+ }
245
+
246
+ for (const percentile of percentiles) {
247
+ const targetCount = Math.ceil((percentile / 100) * stats.count);
248
+ let cumulativeCount = 0;
249
+
250
+ for (const [bucket, count] of Object.entries(stats.buckets)) {
251
+ cumulativeCount += count;
252
+ if (cumulativeCount >= targetCount) {
253
+ result[`p${percentile}`] =
254
+ bucket === '+Inf' ? Infinity : parseFloat(bucket);
255
+ break;
256
+ }
257
+ }
258
+ }
259
+
260
+ return result;
261
+ }
262
+
263
+ /**
264
+ * Generate label key from label values
265
+ */
266
+ _getLabelKey(labelValues) {
267
+ const sortedKeys = Object.keys(labelValues).sort();
268
+ return sortedKeys.map((key) => `${key}=${labelValues[key]}`).join(',');
269
+ }
270
+
271
+ /**
272
+ * Reset histogram
273
+ */
274
+ reset(labelValues = {}) {
275
+ if (Object.keys(labelValues).length === 0) {
276
+ this.sum = 0;
277
+ this.count = 0;
278
+ for (const bucket of this.buckets) {
279
+ this.bucketCounts.set(bucket, 0);
280
+ }
281
+ this.bucketCounts.set('+Inf', 0);
282
+ } else {
283
+ const labelKey = this._getLabelKey(labelValues);
284
+ this.labeledMetrics.delete(labelKey);
285
+ }
286
+
287
+ this.emit('reset', { labelValues, timestamp: Date.now() });
288
+ }
289
+
290
+ /**
291
+ * Export metric data
292
+ */
293
+ export() {
294
+ const labeledData = {};
295
+ for (const [labelKey, metric] of this.labeledMetrics) {
296
+ labeledData[labelKey] = {
297
+ count: metric.count,
298
+ sum: metric.sum,
299
+ buckets: Object.fromEntries(metric.bucketCounts),
300
+ };
301
+ }
302
+
303
+ return {
304
+ name: this.name,
305
+ description: this.description,
306
+ type: 'histogram',
307
+ buckets: this.buckets,
308
+ count: this.count,
309
+ sum: this.sum,
310
+ bucketCounts: Object.fromEntries(this.bucketCounts),
311
+ labeledMetrics: labeledData,
312
+ created: this.created,
313
+ timestamp: Date.now(),
314
+ };
315
+ }
316
+ }
317
+
318
+ /**
319
+ * Pipeline-specific metrics collector
320
+ */
321
+ class PipelineMetrics extends EventEmitter {
322
+ constructor(pipelineId, options = {}) {
323
+ super();
324
+
325
+ this.pipelineId = pipelineId;
326
+ this.options = {
327
+ enableDetailedMetrics: options.enableDetailedMetrics !== false,
328
+ enablePerformanceMetrics: options.enablePerformanceMetrics !== false,
329
+ enableErrorMetrics: options.enableErrorMetrics !== false,
330
+ retentionPeriod: options.retentionPeriod || 24 * 60 * 60 * 1000, // 24 hours
331
+ ...options,
332
+ };
333
+
334
+ this.metrics = new Map();
335
+ this.startTime = Date.now();
336
+ this.executionHistory = [];
337
+
338
+ this.initializeMetrics();
339
+ }
340
+
341
+ /**
342
+ * Initialize standard pipeline metrics
343
+ */
344
+ initializeMetrics() {
345
+ // Execution counters
346
+ this.metrics.set(
347
+ 'executions_total',
348
+ new Counter(
349
+ 'pipeline_executions_total',
350
+ 'Total number of pipeline executions',
351
+ ['status', 'component'],
352
+ ),
353
+ );
354
+
355
+ this.metrics.set(
356
+ 'documents_processed',
357
+ new Counter(
358
+ 'pipeline_documents_processed_total',
359
+ 'Total number of documents processed',
360
+ ['component', 'source'],
361
+ ),
362
+ );
363
+
364
+ this.metrics.set(
365
+ 'errors_total',
366
+ new Counter('pipeline_errors_total', 'Total number of errors', [
367
+ 'component',
368
+ 'error_type',
369
+ ]),
370
+ );
371
+
372
+ // Duration histograms
373
+ this.metrics.set(
374
+ 'execution_duration',
375
+ new Histogram(
376
+ 'pipeline_execution_duration_seconds',
377
+ 'Pipeline execution duration in seconds',
378
+ [0.1, 0.5, 1, 2, 5, 10, 30, 60],
379
+ ),
380
+ );
381
+
382
+ this.metrics.set(
383
+ 'component_duration',
384
+ new Histogram(
385
+ 'pipeline_component_duration_seconds',
386
+ 'Component execution duration in seconds',
387
+ [0.01, 0.05, 0.1, 0.5, 1, 2, 5],
388
+ ),
389
+ );
390
+
391
+ // Performance metrics
392
+ if (this.options.enablePerformanceMetrics) {
393
+ this.metrics.set(
394
+ 'memory_usage',
395
+ new Histogram(
396
+ 'pipeline_memory_usage_bytes',
397
+ 'Memory usage during pipeline execution',
398
+ [1e6, 10e6, 50e6, 100e6, 500e6, 1e9], // 1MB to 1GB
399
+ ),
400
+ );
401
+
402
+ this.metrics.set(
403
+ 'throughput',
404
+ new Histogram(
405
+ 'pipeline_throughput_docs_per_second',
406
+ 'Document processing throughput',
407
+ [1, 5, 10, 50, 100, 500],
408
+ ),
409
+ );
410
+ }
411
+ }
412
+
413
+ /**
414
+ * Record pipeline execution start
415
+ */
416
+ recordExecutionStart(executionId, metadata = {}) {
417
+ const execution = {
418
+ id: executionId,
419
+ startTime: Date.now(),
420
+ endTime: null,
421
+ status: 'running',
422
+ metadata,
423
+ components: [],
424
+ errors: [],
425
+ };
426
+
427
+ this.executionHistory.push(execution);
428
+ this.emit('executionStarted', { executionId, execution });
429
+
430
+ return execution;
431
+ }
432
+
433
+ /**
434
+ * Record pipeline execution completion
435
+ */
436
+ recordExecutionEnd(executionId, status = 'success', metadata = {}) {
437
+ const execution = this.executionHistory.find((e) => e.id === executionId);
438
+ if (!execution) {
439
+ throw new Error(`Execution ${executionId} not found`);
440
+ }
441
+
442
+ execution.endTime = Date.now();
443
+ execution.status = status;
444
+ execution.metadata = { ...execution.metadata, ...metadata };
445
+
446
+ const duration = (execution.endTime - execution.startTime) / 1000; // Convert to seconds
447
+
448
+ // Update metrics
449
+ this.metrics.get('executions_total').inc(1, { status });
450
+ this.metrics.get('execution_duration').observe(duration);
451
+
452
+ this.emit('executionCompleted', { executionId, execution, duration });
453
+
454
+ return execution;
455
+ }
456
+
457
+ /**
458
+ * Record component execution
459
+ */
460
+ recordComponentExecution(
461
+ executionId,
462
+ componentId,
463
+ componentType,
464
+ duration,
465
+ status = 'success',
466
+ metadata = {},
467
+ ) {
468
+ const execution = this.executionHistory.find((e) => e.id === executionId);
469
+ if (execution) {
470
+ execution.components.push({
471
+ id: componentId,
472
+ type: componentType,
473
+ duration,
474
+ status,
475
+ metadata,
476
+ timestamp: Date.now(),
477
+ });
478
+ }
479
+
480
+ // Update metrics
481
+ this.metrics
482
+ .get('executions_total')
483
+ .inc(1, { status, component: componentType });
484
+ this.metrics
485
+ .get('component_duration')
486
+ .observe(duration / 1000, { component: componentType });
487
+
488
+ this.emit('componentExecuted', {
489
+ executionId,
490
+ componentId,
491
+ componentType,
492
+ duration,
493
+ status,
494
+ });
495
+ }
496
+
497
+ /**
498
+ * Record document processing
499
+ */
500
+ recordDocumentProcessed(componentType, source = 'unknown', count = 1) {
501
+ this.metrics
502
+ .get('documents_processed')
503
+ .inc(count, { component: componentType, source });
504
+ this.emit('documentsProcessed', { componentType, source, count });
505
+ }
506
+
507
+ /**
508
+ * Record error
509
+ */
510
+ recordError(componentType, errorType, error, executionId = null) {
511
+ this.metrics
512
+ .get('errors_total')
513
+ .inc(1, { component: componentType, error_type: errorType });
514
+
515
+ if (executionId) {
516
+ const execution = this.executionHistory.find((e) => e.id === executionId);
517
+ if (execution) {
518
+ execution.errors.push({
519
+ componentType,
520
+ errorType,
521
+ message: error.message,
522
+ timestamp: Date.now(),
523
+ });
524
+ }
525
+ }
526
+
527
+ this.emit('errorRecorded', {
528
+ componentType,
529
+ errorType,
530
+ error,
531
+ executionId,
532
+ });
533
+ }
534
+
535
+ /**
536
+ * Record performance metrics
537
+ */
538
+ recordPerformanceMetrics(memoryUsage, throughput) {
539
+ if (this.options.enablePerformanceMetrics) {
540
+ if (memoryUsage !== undefined) {
541
+ this.metrics.get('memory_usage').observe(memoryUsage);
542
+ }
543
+
544
+ if (throughput !== undefined) {
545
+ this.metrics.get('throughput').observe(throughput);
546
+ }
547
+ }
548
+
549
+ this.emit('performanceRecorded', { memoryUsage, throughput });
550
+ }
551
+
552
+ /**
553
+ * Get metric by name
554
+ */
555
+ getMetric(name) {
556
+ return this.metrics.get(name);
557
+ }
558
+
559
+ /**
560
+ * Get all metrics
561
+ */
562
+ getAllMetrics() {
563
+ const result = {};
564
+ for (const [name, metric] of this.metrics) {
565
+ result[name] = metric.export();
566
+ }
567
+ return result;
568
+ }
569
+
570
+ /**
571
+ * Get pipeline statistics
572
+ */
573
+ getStatistics() {
574
+ const now = Date.now();
575
+ const uptime = now - this.startTime;
576
+
577
+ const totalExecutions = this.metrics.get('executions_total').get();
578
+ const totalErrors = this.metrics.get('errors_total').get();
579
+ const totalDocuments = this.metrics.get('documents_processed').get();
580
+
581
+ const executionStats = this.metrics.get('execution_duration').getStats();
582
+ const componentStats = this.metrics.get('component_duration').getStats();
583
+
584
+ return {
585
+ pipelineId: this.pipelineId,
586
+ uptime,
587
+ totalExecutions,
588
+ totalErrors,
589
+ totalDocuments,
590
+ errorRate:
591
+ totalExecutions > 0 ? (totalErrors / totalExecutions) * 100 : 0,
592
+ averageExecutionTime: executionStats.average,
593
+ averageComponentTime: componentStats.average,
594
+ throughput: uptime > 0 ? totalDocuments / (uptime / 1000) : 0, // docs per second
595
+ recentExecutions: this.executionHistory.slice(-10),
596
+ timestamp: now,
597
+ };
598
+ }
599
+
600
+ /**
601
+ * Export all metrics data
602
+ */
603
+ exportMetrics(format = 'json') {
604
+ const data = {
605
+ pipelineId: this.pipelineId,
606
+ timestamp: Date.now(),
607
+ uptime: Date.now() - this.startTime,
608
+ metrics: this.getAllMetrics(),
609
+ statistics: this.getStatistics(),
610
+ executionHistory: this.executionHistory,
611
+ };
612
+
613
+ if (format === 'prometheus') {
614
+ return this._exportPrometheusFormat(data);
615
+ }
616
+
617
+ return data;
618
+ }
619
+
620
+ /**
621
+ * Export metrics in Prometheus format
622
+ */
623
+ _exportPrometheusFormat(data) {
624
+ const lines = [];
625
+
626
+ for (const [name, metric] of Object.entries(data.metrics)) {
627
+ lines.push(`# HELP ${metric.name} ${metric.description}`);
628
+ lines.push(`# TYPE ${metric.name} ${metric.type}`);
629
+
630
+ if (metric.type === 'counter') {
631
+ if (
632
+ metric.labeledValues &&
633
+ Object.keys(metric.labeledValues).length > 0
634
+ ) {
635
+ for (const [labels, value] of Object.entries(metric.labeledValues)) {
636
+ lines.push(`${metric.name}{${labels}} ${value}`);
637
+ }
638
+ } else {
639
+ lines.push(`${metric.name} ${metric.value}`);
640
+ }
641
+ } else if (metric.type === 'histogram') {
642
+ // Histogram buckets
643
+ for (const [bucket, count] of Object.entries(metric.bucketCounts)) {
644
+ const le = bucket === '+Inf' ? '+Inf' : bucket;
645
+ lines.push(`${metric.name}_bucket{le="${le}"} ${count}`);
646
+ }
647
+ lines.push(`${metric.name}_sum ${metric.sum}`);
648
+ lines.push(`${metric.name}_count ${metric.count}`);
649
+ }
650
+
651
+ lines.push('');
652
+ }
653
+
654
+ return lines.join('\n');
655
+ }
656
+
657
+ /**
658
+ * Clean up old execution history
659
+ */
660
+ cleanup() {
661
+ const cutoffTime = Date.now() - this.options.retentionPeriod;
662
+ this.executionHistory = this.executionHistory.filter(
663
+ (e) => e.startTime > cutoffTime,
664
+ );
665
+
666
+ this.emit('cleanup', {
667
+ removedCount: this.executionHistory.length,
668
+ retainedCount: this.executionHistory.length,
669
+ });
670
+ }
671
+
672
+ /**
673
+ * Reset all metrics
674
+ */
675
+ reset() {
676
+ for (const metric of this.metrics.values()) {
677
+ metric.reset();
678
+ }
679
+
680
+ this.executionHistory = [];
681
+ this.startTime = Date.now();
682
+
683
+ this.emit('reset', { timestamp: Date.now() });
684
+ }
685
+ }
686
+
687
+ module.exports = {
688
+ Counter,
689
+ Histogram,
690
+ PipelineMetrics,
691
+ };