@devilsdev/rag-pipeline-utils 2.4.5 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/.ragrc.schema.json +83 -192
  2. package/CHANGELOG.md +431 -0
  3. package/README.md +418 -117
  4. package/SECURITY.md +5 -5
  5. package/dist/experimental.cjs +2 -0
  6. package/dist/experimental.d.ts +1 -0
  7. package/dist/experimental.mjs +5 -0
  8. package/dist/index.cjs +6442 -1355
  9. package/dist/index.d.ts +129 -2
  10. package/dist/index.mjs +1 -1
  11. package/dist/testing.cjs +2 -0
  12. package/dist/testing.d.ts +1 -0
  13. package/dist/testing.mjs +5 -0
  14. package/dist/types/adapters/ai-sdk.d.ts +53 -0
  15. package/dist/types/agentic/agentic-pipeline.d.ts +103 -0
  16. package/dist/types/agentic/iterative-retriever.d.ts +85 -0
  17. package/dist/types/agentic/query-planner.d.ts +145 -0
  18. package/dist/types/agentic/self-critique.d.ts +45 -0
  19. package/dist/types/ai/adaptive-retrieval.d.ts +44 -0
  20. package/dist/types/ai/federation/federated-learning-coordinator.d.ts +62 -0
  21. package/dist/types/ai/multimodal-processing.d.ts +32 -0
  22. package/dist/types/ai/multimodal.d.ts +39 -0
  23. package/dist/types/ai/retrieval-engine.d.ts +37 -0
  24. package/dist/types/ai/training/model-training-orchestrator.d.ts +31 -0
  25. package/dist/types/cache/component-keys.d.ts +7 -0
  26. package/dist/types/cache/memory-cache.d.ts +54 -0
  27. package/dist/types/cache/pipeline-cache.d.ts +66 -0
  28. package/dist/types/cache/redis-cache.d.ts +46 -0
  29. package/dist/types/chunking/chunking-engine.d.ts +70 -0
  30. package/dist/types/chunking/strategies/fixed-size.d.ts +17 -0
  31. package/dist/types/chunking/strategies/recursive.d.ts +20 -0
  32. package/dist/types/chunking/strategies/semantic.d.ts +19 -0
  33. package/dist/types/chunking/strategies/sentence.d.ts +13 -0
  34. package/dist/types/chunking/strategies/structure-aware.d.ts +19 -0
  35. package/dist/types/citation/citation-tracker.d.ts +64 -0
  36. package/dist/types/citation/hallucination-detector.d.ts +40 -0
  37. package/dist/types/citation/source-mapper.d.ts +37 -0
  38. package/dist/types/connectors/anthropic-connector.d.ts +97 -0
  39. package/dist/types/connectors/base-connector.d.ts +87 -0
  40. package/dist/types/connectors/cohere-connector.d.ts +94 -0
  41. package/dist/types/connectors/connector-utils.d.ts +110 -0
  42. package/dist/types/connectors/local-embedder.d.ts +95 -0
  43. package/dist/types/connectors/memory-retriever.d.ts +99 -0
  44. package/dist/types/connectors/ollama-connector.d.ts +115 -0
  45. package/dist/types/connectors/openai-connector.d.ts +129 -0
  46. package/dist/types/connectors/rag-prompt.d.ts +40 -0
  47. package/dist/types/core/create-pipeline.d.ts +8 -0
  48. package/dist/types/core/plugin-registry.d.ts +136 -0
  49. package/dist/types/core/stage-contracts.d.ts +36 -0
  50. package/dist/types/core/validate-plugin.d.ts +45 -0
  51. package/dist/types/cost/cost-calculator.d.ts +152 -0
  52. package/dist/types/cost/cost-tracker.d.ts +126 -0
  53. package/dist/types/cost/token-budget.d.ts +110 -0
  54. package/dist/types/dag/core/error-context.d.ts +239 -0
  55. package/dist/types/dag/core/execution-scheduler.d.ts +191 -0
  56. package/dist/types/dag/core/topology-validation.d.ts +226 -0
  57. package/dist/types/dag/dag-engine.d.ts +156 -0
  58. package/dist/types/dag/dag-node.d.ts +37 -0
  59. package/dist/types/dashboard/dashboard-generator.d.ts +127 -0
  60. package/dist/types/dashboard/metrics-aggregator.d.ts +218 -0
  61. package/dist/types/debug/execution-tracer.d.ts +57 -0
  62. package/dist/types/debug/trace-inspector.d.ts +31 -0
  63. package/dist/types/ecosystem/plugin-certification.d.ts +169 -0
  64. package/dist/types/enterprise/data-governance.d.ts +203 -0
  65. package/dist/types/enterprise/multi-tenancy.d.ts +137 -0
  66. package/dist/types/enterprise/sso-integration.d.ts +420 -0
  67. package/dist/types/evaluate/context-metrics.d.ts +55 -0
  68. package/dist/types/evaluate/faithfulness.d.ts +41 -0
  69. package/dist/types/evaluate/groundedness.d.ts +60 -0
  70. package/dist/types/evaluate/pipeline-evaluator.d.ts +88 -0
  71. package/dist/types/evaluate/relevance.d.ts +21 -0
  72. package/dist/types/evaluate/scoring.d.ts +123 -0
  73. package/dist/types/evaluate/semantic-similarity.d.ts +14 -0
  74. package/dist/types/experimental.d.ts +15 -0
  75. package/dist/types/graph/entity-extractor.d.ts +84 -0
  76. package/dist/types/graph/graph-index.d.ts +49 -0
  77. package/dist/types/graph/graph-retriever.d.ts +81 -0
  78. package/dist/types/graph/knowledge-graph.d.ts +129 -0
  79. package/dist/types/grounding/check-answer.d.ts +101 -0
  80. package/dist/types/guardrails/guardrails-pipeline.d.ts +63 -0
  81. package/dist/types/guardrails/injection-detector.d.ts +47 -0
  82. package/dist/types/guardrails/post-generation-guard.d.ts +61 -0
  83. package/dist/types/guardrails/pre-retrieval-guard.d.ts +64 -0
  84. package/dist/types/guardrails/retrieval-guard.d.ts +57 -0
  85. package/dist/types/mcp/mcp-server.d.ts +85 -0
  86. package/dist/types/mcp/mcp-tool-builder.d.ts +55 -0
  87. package/dist/types/reranker/cascade-reranker.d.ts +40 -0
  88. package/dist/types/reranker/embedding-reranker.d.ts +77 -0
  89. package/dist/types/reranker/scoring-reranker.d.ts +41 -0
  90. package/dist/types/retrieval/bm25-search.d.ts +43 -0
  91. package/dist/types/retrieval/file-vector-store.d.ts +99 -0
  92. package/dist/types/retrieval/hybrid-retriever.d.ts +35 -0
  93. package/dist/types/retrieval/rank-fusion.d.ts +10 -0
  94. package/dist/types/security/jwt-config.d.ts +80 -0
  95. package/dist/types/security/jwt-validator.d.ts +195 -0
  96. package/dist/types/security/plugin-sandbox.d.ts +158 -0
  97. package/dist/types/security/plugin-signature-verifier.d.ts +139 -0
  98. package/dist/types/security/validator.d.ts +5 -0
  99. package/dist/types/streaming/embedding-stream.d.ts +65 -0
  100. package/dist/types/streaming/sse-adapter.d.ts +59 -0
  101. package/dist/types/streaming/stream-router.d.ts +88 -0
  102. package/dist/types/streaming/streaming-embedder.d.ts +99 -0
  103. package/dist/types/streaming/websocket-adapter.d.ts +71 -0
  104. package/dist/types/templates/scaffold.d.ts +55 -0
  105. package/dist/types/templates/template-registry.d.ts +63 -0
  106. package/dist/types/testing.d.ts +168 -0
  107. package/dist/types/utils/embed-query.d.ts +20 -0
  108. package/dist/types/utils/logger.d.ts +50 -0
  109. package/dist/types/utils/rate-limiter.d.ts +66 -0
  110. package/dist/types/utils/retry.d.ts +27 -0
  111. package/dist/types/utils/run-abort.d.ts +34 -0
  112. package/dist/types/utils/secure-logging.d.ts +51 -0
  113. package/dist/types/utils/structured-logger.d.ts +126 -0
  114. package/package.json +72 -33
  115. package/src/adapters/ai-sdk.js +215 -0
  116. package/src/agentic/agentic-pipeline.js +75 -7
  117. package/src/agentic/iterative-retriever.js +47 -10
  118. package/src/agentic/query-planner.js +329 -78
  119. package/src/cache/component-keys.js +38 -0
  120. package/src/cache/memory-cache.js +87 -0
  121. package/src/cache/pipeline-cache.js +260 -0
  122. package/src/cache/redis-cache.js +83 -0
  123. package/src/citation/citation-tracker.js +6 -4
  124. package/src/citation/hallucination-detector.js +2 -2
  125. package/src/citation/source-mapper.js +15 -28
  126. package/src/cli/commands/ai.js +106 -0
  127. package/src/cli/commands/plugin-hub/event-handlers.js +6 -0
  128. package/src/cli/commands/plugin-hub/hub-commands.js +0 -74
  129. package/src/cli/commands/plugin-hub/local-commands.js +143 -0
  130. package/src/cli/commands/plugin-hub.js +114 -49
  131. package/src/cli/diagnostics/config-checker.js +76 -63
  132. package/src/cli/diagnostics/environment-checker.js +62 -31
  133. package/src/cli/diagnostics/plugin-checker.js +36 -89
  134. package/src/cli/doctor-command.js +39 -5
  135. package/src/cli/enhanced-cli-commands.js +104 -233
  136. package/src/cli/handlers/ai-handler.js +364 -0
  137. package/src/cli/handlers/diagnostics-handler.js +78 -31
  138. package/src/cli/handlers/ingest-handler.js +28 -22
  139. package/src/cli/handlers/init-handler.js +148 -25
  140. package/src/cli/handlers/query-handler.js +203 -25
  141. package/src/cli/handlers/run-handler.js +16 -167
  142. package/src/cli/handlers/serve-handler.js +60 -0
  143. package/src/cli/services/bench-stats.js +54 -0
  144. package/src/cli/services/benchmark.js +161 -0
  145. package/src/cli/services/feedback.js +155 -0
  146. package/src/cli/services/instrument.js +83 -0
  147. package/src/cli/services/rag-service.js +361 -0
  148. package/src/cli/shared/cli-utils.js +24 -7
  149. package/src/cli/shared/config-context.js +21 -0
  150. package/src/cli/wizard/config-builder.js +81 -0
  151. package/src/cli/wizard/init-wizard.js +602 -0
  152. package/src/config/component-factory.js +125 -0
  153. package/src/config/enhanced-ragrc-schema.js +0 -2
  154. package/src/config/load-config.js +29 -10
  155. package/src/config/load-plugin-config.js +46 -2
  156. package/src/config/validate-schema.js +4 -1
  157. package/src/connectors/anthropic-connector.js +115 -57
  158. package/src/connectors/base-connector.js +74 -19
  159. package/src/connectors/cohere-connector.js +108 -34
  160. package/src/connectors/connector-utils.js +302 -0
  161. package/src/connectors/local-embedder.js +63 -5
  162. package/src/connectors/memory-retriever.js +125 -14
  163. package/src/connectors/ollama-connector.js +112 -51
  164. package/src/connectors/openai-connector.js +153 -44
  165. package/src/connectors/rag-prompt.js +88 -0
  166. package/src/core/create-pipeline.js +307 -61
  167. package/src/core/performance/parallel-processor.js +62 -1
  168. package/src/core/plugin-registry.js +125 -26
  169. package/src/core/stage-contracts.js +36 -0
  170. package/src/core/validate-plugin.js +85 -0
  171. package/src/cost/cost-calculator.js +205 -32
  172. package/src/debug/execution-tracer.js +14 -4
  173. package/src/dev/dev-server.js +173 -11
  174. package/src/dev/hot-reload.js +44 -4
  175. package/src/ecosystem/plugin-checks.js +181 -0
  176. package/src/ecosystem/plugin-hub.js +50 -125
  177. package/src/ecosystem/plugin-installer.js +298 -0
  178. package/src/ecosystem/plugin-probe.js +127 -0
  179. package/src/ecosystem/plugin-scanner.js +185 -0
  180. package/src/ecosystem/tar-extract.js +114 -0
  181. package/src/enterprise/audit-logging.js +158 -29
  182. package/src/enterprise/sso-integration.js +716 -228
  183. package/src/evaluate/context-metrics.js +28 -16
  184. package/src/evaluate/evaluator.js +3 -3
  185. package/src/evaluate/faithfulness.js +18 -14
  186. package/src/evaluate/groundedness.js +43 -12
  187. package/src/evaluate/pipeline-evaluator.js +35 -8
  188. package/src/evaluate/scoring.js +251 -3
  189. package/src/evaluate/semantic-similarity.js +79 -0
  190. package/src/experimental.js +71 -0
  191. package/src/grounding/check-answer.js +173 -0
  192. package/src/guardrails/guardrails-pipeline.js +69 -7
  193. package/src/guardrails/injection-detector.js +283 -0
  194. package/src/guardrails/post-generation-guard.js +19 -0
  195. package/src/guardrails/pre-retrieval-guard.js +36 -36
  196. package/src/guardrails/retrieval-guard.js +58 -8
  197. package/src/index.js +58 -1
  198. package/src/loader/csv-loader.js +3 -2
  199. package/src/loader/document-reader.js +91 -0
  200. package/src/loader/html-loader.js +3 -1
  201. package/src/loader/markdown-loader.js +3 -2
  202. package/src/reranker/cascade-reranker.js +3 -0
  203. package/src/reranker/embedding-reranker.js +136 -21
  204. package/src/reranker/llm-reranker.js +79 -12
  205. package/src/reranker/scoring-reranker.js +3 -0
  206. package/src/retrieval/bm25-search.js +12 -6
  207. package/src/retrieval/file-vector-store.js +241 -0
  208. package/src/retrieval/hybrid-retriever.js +22 -19
  209. package/src/security/jwt-config.js +13 -2
  210. package/src/security/jwt-validator.js +139 -34
  211. package/src/security/plugin-sandbox.js +18 -1
  212. package/src/security/plugin-signature-verifier.js +245 -70
  213. package/src/testing.js +254 -0
  214. package/src/utils/deprecate.js +69 -0
  215. package/src/utils/embed-query.js +33 -0
  216. package/src/utils/input-sanitizer.js +19 -4
  217. package/src/utils/logger.js +34 -4
  218. package/src/utils/run-abort.js +102 -0
  219. package/src/ai/federated-learning.js +0 -211
  220. package/src/ai/federation.js +0 -71
  221. package/src/ai/index.js +0 -22
  222. package/src/ai/model-trainer.js +0 -217
  223. package/src/ai/model-training.js +0 -202
  224. package/src/ai/multimodal/multi-modal-processor.js +0 -345
  225. package/src/ai/orchestrator.js +0 -65
  226. package/src/ai/retrieval/adaptive-retrieval-engine.js +0 -398
  227. package/src/cli/commands/ai-ml/adaptive-commands.js +0 -178
  228. package/src/cli/commands/ai-ml/federated-commands.js +0 -207
  229. package/src/cli/commands/ai-ml/index.js +0 -17
  230. package/src/cli/commands/ai-ml/multimodal-commands.js +0 -187
  231. package/src/cli/commands/ai-ml/shared.js +0 -51
  232. package/src/cli/commands/ai-ml/training-commands.js +0 -228
  233. package/src/cli/commands/ai-ml.js +0 -386
  234. package/src/cli/commands/docs.js +0 -228
  235. package/src/cli/commands/dx.js +0 -538
  236. package/src/cli/commands/wizard.js +0 -627
  237. package/src/cli/input-sanitization-middleware.js +0 -356
  238. package/src/cli/interactive-wizard.js +0 -373
  239. package/src/cli/templates/development.template.json +0 -64
  240. package/src/cli/templates/minimal.template.json +0 -33
  241. package/src/cli/templates/production.template.json +0 -104
  242. package/src/cli/templates/testing.template.json +0 -45
  243. package/src/cli/wizard/configuration.js +0 -148
  244. package/src/cli/wizard/observability.js +0 -79
  245. package/src/cli/wizard/performance.js +0 -76
  246. package/src/cli/wizard/plugin-selection.js +0 -297
  247. package/src/cli/wizard/preview-save.js +0 -172
  248. package/src/cli/wizard/project-setup.js +0 -66
  249. package/src/core/pipeline-factory.js +0 -59
  250. package/src/dag/enhanced-dag-engine.js +0 -894
  251. package/src/dx/index.js +0 -40
  252. package/src/dx/integration-templates.js +0 -662
  253. package/src/dx/performance-profiler.js +0 -968
  254. package/src/dx/realtime-debugger.js +0 -755
  255. package/src/dx/visual-pipeline-builder.js +0 -578
  256. package/src/ingest.js +0 -59
  257. package/src/pipeline/pipeline.js +0 -18
  258. package/src/query.js +0 -117
@@ -1,968 +0,0 @@
1
- /**
2
- * Performance Profiler
3
- *
4
- * Detailed bottleneck analysis and performance monitoring for RAG pipelines.
5
- * Provides comprehensive metrics, flame graphs, and optimization recommendations.
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 PerformanceProfiler extends EventEmitter {
16
- constructor(_options = {}) {
17
- super();
18
-
19
- this._options = {
20
- enableCPUProfiling: _options.enableCPUProfiling !== false,
21
- enableMemoryProfiling: _options.enableMemoryProfiling !== false,
22
- enableNetworkProfiling: _options.enableNetworkProfiling !== false,
23
- sampleInterval: _options.sampleInterval || 100, // ms
24
- maxSamples: _options.maxSamples || 10000,
25
- outputDir: _options.outputDir || "./profiling-reports",
26
- ..._options,
27
- };
28
-
29
- this.profiles = new Map();
30
- this.currentProfile = null;
31
- this.samples = [];
32
- this.metrics = new Map();
33
- this.isProfileing = false;
34
-
35
- this.initializeProfiler();
36
- }
37
-
38
- /**
39
- * Initialize profiler components
40
- */
41
- initializeProfiler() {
42
- this.baselineMetrics = {
43
- memory: process.memoryUsage(),
44
- cpu: process.cpuUsage(),
45
- timestamp: Date.now(),
46
- };
47
-
48
- this.performanceCounters = {
49
- totalExecutions: 0,
50
- totalDuration: 0,
51
- averageDuration: 0,
52
- minDuration: Infinity,
53
- maxDuration: 0,
54
- errorCount: 0,
55
- successCount: 0,
56
- };
57
- }
58
-
59
- /**
60
- * Start profiling session
61
- */
62
- startProfiling(sessionId, _config = {}) {
63
- if (this.isProfileing) {
64
- throw new Error("Profiling session already active");
65
- }
66
-
67
- const profile = {
68
- id: sessionId,
69
- _config,
70
- startTime: Date.now(),
71
- endTime: null,
72
- samples: [],
73
- metrics: new Map(),
74
- components: new Map(),
75
- flamegraph: null,
76
- recommendations: [],
77
- status: "active",
78
- };
79
-
80
- this.profiles.set(sessionId, profile);
81
- this.currentProfile = profile;
82
- this.isProfileing = true;
83
- this.samples = [];
84
-
85
- // Start sampling if enabled
86
- if (
87
- this._options.enableCPUProfiling ||
88
- this._options.enableMemoryProfiling
89
- ) {
90
- this.startSampling();
91
- }
92
-
93
- this.emit("profilingStarted", { sessionId, profile });
94
-
95
- return sessionId;
96
- }
97
-
98
- /**
99
- * Start performance sampling
100
- */
101
- startSampling() {
102
- this.samplingInterval = setInterval(() => {
103
- if (
104
- !this.isProfileing ||
105
- this.samples.length >= this._options.maxSamples
106
- ) {
107
- return;
108
- }
109
-
110
- const sample = this.collectSample();
111
- this.samples.push(sample);
112
- this.currentProfile.samples.push(sample);
113
- }, this._options.sampleInterval);
114
- }
115
-
116
- /**
117
- * Collect performance sample
118
- */
119
- collectSample() {
120
- const timestamp = Date.now();
121
- const sample = {
122
- timestamp,
123
- relativeTime: timestamp - this.currentProfile.startTime,
124
- };
125
-
126
- // CPU metrics
127
- if (this._options.enableCPUProfiling) {
128
- const cpuUsage = process.cpuUsage();
129
- sample.cpu = {
130
- user: cpuUsage.user,
131
- system: cpuUsage.system,
132
- userDelta: cpuUsage.user - this.baselineMetrics.cpu.user,
133
- systemDelta: cpuUsage.system - this.baselineMetrics.cpu.system,
134
- };
135
- }
136
-
137
- // Memory metrics
138
- if (this._options.enableMemoryProfiling) {
139
- const memoryUsage = process.memoryUsage();
140
- sample.memory = {
141
- rss: memoryUsage.rss,
142
- heapTotal: memoryUsage.heapTotal,
143
- heapUsed: memoryUsage.heapUsed,
144
- external: memoryUsage.external,
145
- arrayBuffers: memoryUsage.arrayBuffers,
146
- heapUsedDelta:
147
- memoryUsage.heapUsed - this.baselineMetrics.memory.heapUsed,
148
- rssDelta: memoryUsage.rss - this.baselineMetrics.memory.rss,
149
- };
150
- }
151
-
152
- // Event loop lag
153
- const start = process.hrtime.bigint();
154
- setImmediate(() => {
155
- const lag = Number(process.hrtime.bigint() - start) / 1000000; // Convert to ms
156
- sample.eventLoopLag = lag;
157
- });
158
-
159
- return sample;
160
- }
161
-
162
- /**
163
- * Profile component execution
164
- */
165
- async profileComponent(
166
- componentId,
167
- componentType,
168
- executionFunc,
169
- input = null,
170
- ) {
171
- if (!this.isProfileing) {
172
- throw new Error("No active profiling session");
173
- }
174
-
175
- const componentProfile = this._createComponentProfile(
176
- componentId,
177
- componentType,
178
- );
179
- const execution = this._createExecution(input);
180
-
181
- componentProfile.executions.push(execution);
182
-
183
- try {
184
- const result = await this._executeWithTiming(executionFunc, execution);
185
- this._updateSuccessMetrics(componentProfile, execution);
186
- this._emitProfiledEvent(componentId, execution.id, execution, null, true);
187
- return result;
188
- } catch (error) {
189
- this._handleExecutionError(componentProfile, execution, error);
190
- this._emitProfiledEvent(
191
- componentId,
192
- execution.id,
193
- execution,
194
- error.message,
195
- false,
196
- );
197
- throw error;
198
- } finally {
199
- this.currentProfile.components.set(componentId, componentProfile);
200
- }
201
- }
202
-
203
- _createComponentProfile(componentId, componentType) {
204
- return {
205
- id: componentId,
206
- _type: componentType,
207
- executions: [],
208
- totalDuration: 0,
209
- averageDuration: 0,
210
- minDuration: Infinity,
211
- maxDuration: 0,
212
- errorCount: 0,
213
- successCount: 0,
214
- memoryPeak: 0,
215
- cpuUsage: { user: 0, system: 0 },
216
- };
217
- }
218
-
219
- _createExecution(input) {
220
- return {
221
- id: `exec_${Date.now()}`,
222
- startTime: Date.now(),
223
- startMemory: process.memoryUsage(),
224
- startCpu: process.cpuUsage(),
225
- input: this.serializeInput(input),
226
- output: null,
227
- error: null,
228
- duration: 0,
229
- memoryUsage: null,
230
- cpuUsage: null,
231
- networkCalls: [],
232
- customMetrics: new Map(),
233
- };
234
- }
235
-
236
- async _executeWithTiming(executionFunc, execution) {
237
- const startHrTime = process.hrtime.bigint();
238
- const result = await executionFunc();
239
- const endHrTime = process.hrtime.bigint();
240
-
241
- execution.duration = Number(endHrTime - startHrTime) / 1000000; // Convert to ms
242
- execution.endTime = Date.now();
243
- execution.endMemory = process.memoryUsage();
244
- execution.endCpu = process.cpuUsage(execution.startCpu);
245
- execution.output = this.serializeOutput(result);
246
-
247
- return result;
248
- }
249
-
250
- _updateSuccessMetrics(componentProfile, execution) {
251
- componentProfile.totalDuration += execution.duration;
252
- componentProfile.successCount++;
253
- componentProfile.minDuration = Math.min(
254
- componentProfile.minDuration,
255
- execution.duration,
256
- );
257
- componentProfile.maxDuration = Math.max(
258
- componentProfile.maxDuration,
259
- execution.duration,
260
- );
261
-
262
- this._updateAverageDuration(componentProfile);
263
- this._updateMemoryMetrics(componentProfile, execution);
264
- this._updateCpuMetrics(componentProfile, execution);
265
- }
266
-
267
- _updateAverageDuration(componentProfile) {
268
- const totalExecutions =
269
- componentProfile.successCount + componentProfile.errorCount;
270
- componentProfile.averageDuration =
271
- componentProfile.totalDuration / totalExecutions;
272
- }
273
-
274
- _updateMemoryMetrics(componentProfile, execution) {
275
- componentProfile.memoryPeak = Math.max(
276
- componentProfile.memoryPeak,
277
- execution.endMemory.heapUsed,
278
- );
279
- }
280
-
281
- _updateCpuMetrics(componentProfile, execution) {
282
- componentProfile.cpuUsage.user += execution.endCpu.user;
283
- componentProfile.cpuUsage.system += execution.endCpu.system;
284
- }
285
-
286
- _handleExecutionError(componentProfile, execution, error) {
287
- execution.error = {
288
- message: error.message,
289
- stack: error.stack,
290
- name: error.name,
291
- };
292
- execution.endTime = Date.now();
293
- execution.duration = execution.endTime - execution.startTime;
294
-
295
- componentProfile.errorCount++;
296
- this._updateAverageDuration(componentProfile);
297
- }
298
-
299
- _emitProfiledEvent(
300
- componentId,
301
- executionId,
302
- execution,
303
- errorMessage,
304
- success,
305
- ) {
306
- const eventData = {
307
- componentId,
308
- executionId,
309
- duration: execution.duration,
310
- success,
311
- };
312
-
313
- if (success) {
314
- const memoryDelta =
315
- execution.endMemory.heapUsed - execution.startMemory.heapUsed;
316
- eventData.memoryDelta = memoryDelta;
317
- } else {
318
- eventData.error = errorMessage;
319
- }
320
-
321
- this.emit("componentProfiled", eventData);
322
- }
323
-
324
- /**
325
- * Add custom metric
326
- */
327
- addMetric(name, value, tags = {}) {
328
- if (!this.isProfileing) {
329
- return;
330
- }
331
-
332
- const metric = {
333
- name,
334
- value,
335
- tags,
336
- timestamp: Date.now(),
337
- relativeTime: Date.now() - this.currentProfile.startTime,
338
- };
339
-
340
- if (!this.currentProfile.metrics.has(name)) {
341
- this.currentProfile.metrics.set(name, []);
342
- }
343
-
344
- this.currentProfile.metrics.get(name).push(metric);
345
- this.emit("metricAdded", { name, value, tags });
346
- }
347
-
348
- /**
349
- * Profile network call
350
- */
351
- profileNetworkCall(
352
- url,
353
- method,
354
- duration,
355
- statusCode,
356
- requestSize = 0,
357
- responseSize = 0,
358
- ) {
359
- if (!this.isProfileing || !this._options.enableNetworkProfiling) {
360
- return;
361
- }
362
-
363
- const networkCall = {
364
- url,
365
- method,
366
- duration,
367
- statusCode,
368
- requestSize,
369
- responseSize,
370
- timestamp: Date.now(),
371
- relativeTime: Date.now() - this.currentProfile.startTime,
372
- };
373
-
374
- // Add to current execution if available
375
- const currentComponent = Array.from(
376
- this.currentProfile.components.values(),
377
- ).pop();
378
- if (currentComponent && currentComponent.executions.length > 0) {
379
- const currentExecution =
380
- currentComponent.executions[currentComponent.executions.length - 1];
381
- currentExecution.networkCalls.push(networkCall);
382
- }
383
-
384
- this.emit("networkCallProfiled", networkCall);
385
- }
386
-
387
- /**
388
- * Stop profiling session
389
- */
390
- stopProfiling() {
391
- if (!this.isProfileing) {
392
- throw new Error("No active profiling session");
393
- }
394
-
395
- this.isProfileing = false;
396
-
397
- if (this.samplingInterval) {
398
- clearInterval(this.samplingInterval);
399
- this.samplingInterval = null;
400
- }
401
-
402
- this.currentProfile.endTime = Date.now();
403
- this.currentProfile.status = "completed";
404
-
405
- // Generate analysis
406
- this.analyzeProfile(this.currentProfile);
407
-
408
- const sessionId = this.currentProfile.id;
409
- this.emit("profilingStopped", { sessionId, profile: this.currentProfile });
410
-
411
- this.currentProfile = null;
412
-
413
- return sessionId;
414
- }
415
-
416
- /**
417
- * Analyze profile data
418
- */
419
- analyzeProfile(profile) {
420
- const analysis = {
421
- summary: this.generateSummary(profile),
422
- bottlenecks: this.identifyBottlenecks(profile),
423
- recommendations: this.generateRecommendations(profile),
424
- flamegraph: this.generateFlamegraph(profile),
425
- trends: this.analyzeTrends(profile),
426
- };
427
-
428
- profile.analysis = analysis;
429
- return analysis;
430
- }
431
-
432
- /**
433
- * Generate profile summary
434
- */
435
- generateSummary(profile) {
436
- const totalDuration = profile.endTime - profile.startTime;
437
- const components = Array.from(profile.components.values());
438
-
439
- const totalExecutions = components.reduce(
440
- (sum, comp) => sum + comp.executions.length,
441
- 0,
442
- );
443
- const totalErrors = components.reduce(
444
- (sum, comp) => sum + comp.errorCount,
445
- 0,
446
- );
447
- const avgComponentDuration =
448
- components.reduce((sum, comp) => sum + comp.averageDuration, 0) /
449
- components.length;
450
-
451
- const memoryPeak = Math.max(
452
- ...profile.samples.map((s) => s.memory?.heapUsed || 0),
453
- );
454
- const memoryGrowth =
455
- profile.samples.length > 0
456
- ? profile.samples[profile.samples.length - 1].memory?.heapUsed -
457
- profile.samples[0].memory?.heapUsed
458
- : 0;
459
-
460
- return {
461
- totalDuration,
462
- totalExecutions,
463
- totalErrors,
464
- errorRate:
465
- totalExecutions > 0 ? (totalErrors / totalExecutions) * 100 : 0,
466
- avgComponentDuration,
467
- memoryPeak,
468
- memoryGrowth,
469
- samplesCollected: profile.samples.length,
470
- componentsProfiled: components.length,
471
- };
472
- }
473
-
474
- /**
475
- * Identify performance bottlenecks
476
- */
477
- identifyBottlenecks(profile) {
478
- const components = Array.from(profile.components.values());
479
- const bottlenecks = [];
480
-
481
- // Slow components
482
- const avgDuration =
483
- components.reduce((sum, comp) => sum + comp.averageDuration, 0) /
484
- components.length;
485
- const slowComponents = components.filter(
486
- (comp) => comp.averageDuration > avgDuration * 2,
487
- );
488
-
489
- slowComponents.forEach((comp) => {
490
- bottlenecks.push({
491
- type: "slow_component",
492
- severity: "high",
493
- component: comp.id,
494
- description: `Component ${comp.id} is ${(comp.averageDuration / avgDuration).toFixed(1)}x slower than average`,
495
- metric: "duration",
496
- value: comp.averageDuration,
497
- threshold: avgDuration * 2,
498
- });
499
- });
500
-
501
- // Memory leaks
502
- if (profile.samples.length > 10) {
503
- const memoryTrend = this.calculateTrend(
504
- profile.samples.map((s) => s.memory?.heapUsed || 0),
505
- );
506
- if (memoryTrend > 0.1) {
507
- // Growing trend
508
- bottlenecks.push({
509
- type: "memory_leak",
510
- severity: "medium",
511
- description:
512
- "Potential memory leak detected - heap usage is consistently growing",
513
- metric: "memory_growth_rate",
514
- value: memoryTrend,
515
- threshold: 0.1,
516
- });
517
- }
518
- }
519
-
520
- // High error rates
521
- const highErrorComponents = components.filter((comp) => {
522
- const totalExecs = comp.successCount + comp.errorCount;
523
- return totalExecs > 0 && comp.errorCount / totalExecs > 0.1;
524
- });
525
-
526
- highErrorComponents.forEach((comp) => {
527
- const errorRate =
528
- (comp.errorCount / (comp.successCount + comp.errorCount)) * 100;
529
- bottlenecks.push({
530
- type: "high_error_rate",
531
- severity: "high",
532
- component: comp.id,
533
- description: `Component ${comp.id} has high error rate: ${errorRate.toFixed(1)}%`,
534
- metric: "error_rate",
535
- value: errorRate,
536
- threshold: 10,
537
- });
538
- });
539
-
540
- // Event loop lag
541
- const avgEventLoopLag =
542
- profile.samples
543
- .filter((s) => s.eventLoopLag !== undefined)
544
- .reduce((sum, s) => sum + s.eventLoopLag, 0) / profile.samples.length;
545
-
546
- if (avgEventLoopLag > 10) {
547
- // More than 10ms average lag
548
- bottlenecks.push({
549
- type: "event_loop_lag",
550
- severity: "medium",
551
- description: `High event loop lag detected: ${avgEventLoopLag.toFixed(1)}ms average`,
552
- metric: "event_loop_lag",
553
- value: avgEventLoopLag,
554
- threshold: 10,
555
- });
556
- }
557
-
558
- return bottlenecks.sort((a, b) => {
559
- const severityOrder = { high: 3, medium: 2, low: 1 };
560
- return severityOrder[b.severity] - severityOrder[a.severity];
561
- });
562
- }
563
-
564
- /**
565
- * Generate optimization recommendations
566
- */
567
- generateRecommendations(profile) {
568
- const recommendations = [];
569
- const bottlenecks = this.identifyBottlenecks(profile);
570
-
571
- bottlenecks.forEach((bottleneck) => {
572
- switch (bottleneck.type) {
573
- case "slow_component":
574
- recommendations.push({
575
- priority: "high",
576
- category: "performance",
577
- title: `Optimize ${bottleneck.component} component`,
578
- description: `Consider caching, parallel processing, or algorithm optimization for ${bottleneck.component}`,
579
- impact: "high",
580
- effort: "medium",
581
- });
582
- break;
583
-
584
- case "memory_leak":
585
- recommendations.push({
586
- priority: "high",
587
- category: "memory",
588
- title: "Investigate memory leak",
589
- description:
590
- "Review object lifecycle management and ensure proper cleanup of resources",
591
- impact: "high",
592
- effort: "high",
593
- });
594
- break;
595
-
596
- case "high_error_rate":
597
- recommendations.push({
598
- priority: "critical",
599
- category: "reliability",
600
- title: `Fix error handling in ${bottleneck.component}`,
601
- description: "Improve error handling and add retry mechanisms",
602
- impact: "high",
603
- effort: "medium",
604
- });
605
- break;
606
-
607
- case "event_loop_lag":
608
- recommendations.push({
609
- priority: "medium",
610
- category: "concurrency",
611
- title: "Reduce event loop blocking",
612
- description:
613
- "Move CPU-intensive operations to worker threads or break into smaller chunks",
614
- impact: "medium",
615
- effort: "medium",
616
- });
617
- break;
618
- }
619
- });
620
-
621
- // General recommendations
622
- const components = Array.from(profile.components.values());
623
- const totalNetworkCalls = components.reduce(
624
- (sum, comp) =>
625
- sum +
626
- comp.executions.reduce(
627
- (execSum, exec) => execSum + exec.networkCalls.length,
628
- 0,
629
- ),
630
- 0,
631
- );
632
-
633
- if (totalNetworkCalls > 10) {
634
- recommendations.push({
635
- priority: "medium",
636
- category: "network",
637
- title: "Optimize network calls",
638
- description:
639
- "Consider request batching, connection pooling, or caching for frequent API calls",
640
- impact: "medium",
641
- effort: "low",
642
- });
643
- }
644
-
645
- return recommendations.sort((a, b) => {
646
- const priorityOrder = { critical: 4, high: 3, medium: 2, low: 1 };
647
- return priorityOrder[b.priority] - priorityOrder[a.priority];
648
- });
649
- }
650
-
651
- /**
652
- * Generate flamegraph data
653
- */
654
- generateFlamegraph(profile) {
655
- const components = Array.from(profile.components.values());
656
- const flamegraphData = {
657
- name: "Pipeline",
658
- value: profile.endTime - profile.startTime,
659
- children: [],
660
- };
661
-
662
- components.forEach((component) => {
663
- const componentNode = {
664
- name: component.id,
665
- value: component.totalDuration,
666
- children: [],
667
- };
668
-
669
- // Group executions by similar duration
670
- const executionGroups = this.groupExecutionsByDuration(
671
- component.executions,
672
- );
673
-
674
- executionGroups.forEach((group) => {
675
- componentNode.children.push({
676
- name: `${group.executions.length} executions`,
677
- value: group.totalDuration,
678
- avgDuration: group.avgDuration,
679
- count: group.executions.length,
680
- });
681
- });
682
-
683
- flamegraphData.children.push(componentNode);
684
- });
685
-
686
- return flamegraphData;
687
- }
688
-
689
- /**
690
- * Group executions by similar duration for flamegraph
691
- */
692
- groupExecutionsByDuration(executions) {
693
- const groups = [];
694
- const sortedExecutions = executions.sort((a, b) => a.duration - b.duration);
695
-
696
- let currentGroup = null;
697
-
698
- sortedExecutions.forEach((execution) => {
699
- if (
700
- !currentGroup ||
701
- Math.abs(execution.duration - currentGroup.avgDuration) >
702
- currentGroup.avgDuration * 0.5
703
- ) {
704
- currentGroup = {
705
- executions: [execution],
706
- totalDuration: execution.duration,
707
- avgDuration: execution.duration,
708
- };
709
- groups.push(currentGroup);
710
- } else {
711
- currentGroup.executions.push(execution);
712
- currentGroup.totalDuration += execution.duration;
713
- currentGroup.avgDuration =
714
- currentGroup.totalDuration / currentGroup.executions.length;
715
- }
716
- });
717
-
718
- return groups;
719
- }
720
-
721
- /**
722
- * Analyze trends in performance data
723
- */
724
- analyzeTrends(profile) {
725
- const trends = {};
726
-
727
- if (profile.samples.length > 5) {
728
- // Memory trend
729
- const memoryValues = profile.samples.map((s) => s.memory?.heapUsed || 0);
730
- trends.memory = {
731
- trend: this.calculateTrend(memoryValues),
732
- direction: this.getTrendDirection(memoryValues),
733
- volatility: this.calculateVolatility(memoryValues),
734
- };
735
-
736
- // CPU trend
737
- const cpuValues = profile.samples.map(
738
- (s) => (s.cpu?.userDelta || 0) + (s.cpu?.systemDelta || 0),
739
- );
740
- trends.cpu = {
741
- trend: this.calculateTrend(cpuValues),
742
- direction: this.getTrendDirection(cpuValues),
743
- volatility: this.calculateVolatility(cpuValues),
744
- };
745
-
746
- // Event loop lag trend
747
- const lagValues = profile.samples
748
- .filter((s) => s.eventLoopLag !== undefined)
749
- .map((s) => s.eventLoopLag);
750
- if (lagValues.length > 0) {
751
- trends.eventLoopLag = {
752
- trend: this.calculateTrend(lagValues),
753
- direction: this.getTrendDirection(lagValues),
754
- volatility: this.calculateVolatility(lagValues),
755
- };
756
- }
757
- }
758
-
759
- return trends;
760
- }
761
-
762
- /**
763
- * Calculate trend (linear regression slope)
764
- */
765
- calculateTrend(values) {
766
- if (values.length < 2) return 0;
767
-
768
- const n = values.length;
769
- const sumX = (n * (n - 1)) / 2;
770
- const sumY = values.reduce((sum, val) => sum + val, 0);
771
- const sumXY = values.reduce((sum, val, i) => sum + i * val, 0);
772
- const sumXX = (n * (n - 1) * (2 * n - 1)) / 6;
773
-
774
- const slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);
775
- return slope;
776
- }
777
-
778
- /**
779
- * Get trend direction
780
- */
781
- getTrendDirection(values) {
782
- const trend = this.calculateTrend(values);
783
- if (Math.abs(trend) < 0.01) return "stable";
784
- return trend > 0 ? "increasing" : "decreasing";
785
- }
786
-
787
- /**
788
- * Calculate volatility (standard deviation)
789
- */
790
- calculateVolatility(values) {
791
- if (values.length < 2) return 0;
792
-
793
- const mean = values.reduce((sum, val) => sum + val, 0) / values.length;
794
- const variance =
795
- values.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) /
796
- values.length;
797
- return Math.sqrt(variance);
798
- }
799
-
800
- /**
801
- * Export profile report
802
- */
803
- async exportReport(sessionId, format = "json") {
804
- const profile = this.profiles.get(sessionId);
805
- if (!profile) {
806
- throw new Error(`Profile ${sessionId} not found`);
807
- }
808
-
809
- await fs.mkdir(this._options.outputDir, { recursive: true });
810
-
811
- const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
812
- const filename = `profile-${sessionId}-${timestamp}`;
813
-
814
- if (format === "json") {
815
- const _filePath = path.join(this._options.outputDir, `${filename}.json`);
816
- await fs.writeFile(_filePath, JSON.stringify(profile, null, 2));
817
- return _filePath;
818
- } else if (format === "html") {
819
- const htmlReport = this.generateHTMLReport(profile);
820
- const _filePath = path.join(this._options.outputDir, `${filename}.html`);
821
- await fs.writeFile(_filePath, htmlReport);
822
- return _filePath;
823
- } else {
824
- throw new Error(`Unsupported format: ${format}`);
825
- }
826
- }
827
-
828
- /**
829
- * Generate HTML report
830
- */
831
- generateHTMLReport(profile) {
832
- const summary = profile.analysis?.summary || {};
833
- const bottlenecks = profile.analysis?.bottlenecks || [];
834
- const recommendations = profile.analysis?.recommendations || [];
835
-
836
- return `<!DOCTYPE html>
837
- <html>
838
- <head>
839
- <title>Performance Profile Report - ${profile.id}</title>
840
- <style>
841
- body { font-family: Arial, sans-serif; margin: 20px; }
842
- .header { background: #f5f5f5; padding: 20px; border-radius: 5px; }
843
- .section { margin: 20px 0; }
844
- .metric { display: inline-block; margin: 10px; padding: 10px; background: #e9ecef; border-radius: 3px; }
845
- .bottleneck { padding: 10px; margin: 5px 0; border-left: 4px solid #dc3545; background: #f8d7da; }
846
- .recommendation { padding: 10px; margin: 5px 0; border-left: 4px solid #28a745; background: #d4edda; }
847
- .high { border-color: #dc3545; }
848
- .medium { border-color: #ffc107; }
849
- .low { border-color: #28a745; }
850
- </style>
851
- </head>
852
- <body>
853
- <div class="header">
854
- <h1>Performance Profile Report</h1>
855
- <p><strong>Session:</strong> ${profile.id}</p>
856
- <p><strong>Duration:</strong> ${summary.totalDuration || 0}ms</p>
857
- <p><strong>Generated:</strong> ${new Date().toISOString()}</p>
858
- </div>
859
-
860
- <div class="section">
861
- <h2>Summary</h2>
862
- <div class="metric">Total Executions: ${summary.totalExecutions || 0}</div>
863
- <div class="metric">Error Rate: ${(summary.errorRate || 0).toFixed(1)}%</div>
864
- <div class="metric">Avg Duration: ${(summary.avgComponentDuration || 0).toFixed(1)}ms</div>
865
- <div class="metric">Memory Peak: ${Math.round((summary.memoryPeak || 0) / 1024 / 1024)}MB</div>
866
- </div>
867
-
868
- <div class="section">
869
- <h2>Bottlenecks</h2>
870
- ${bottlenecks
871
- .map(
872
- (b) => `
873
- <div class="bottleneck ${b.severity}">
874
- <strong>${b.type.replace(/_/g, " ").toUpperCase()}</strong>: ${b.description}
875
- </div>
876
- `,
877
- )
878
- .join("")}
879
- </div>
880
-
881
- <div class="section">
882
- <h2>Recommendations</h2>
883
- ${recommendations
884
- .map(
885
- (r) => `
886
- <div class="recommendation ${r.priority}">
887
- <strong>${r.title}</strong>: ${r.description}
888
- <br><small>Impact: ${r.impact}, Effort: ${r.effort}</small>
889
- </div>
890
- `,
891
- )
892
- .join("")}
893
- </div>
894
- </body>
895
- </html>`;
896
- }
897
-
898
- /**
899
- * Serialize input for storage
900
- */
901
- serializeInput(input) {
902
- try {
903
- return JSON.parse(JSON.stringify(input));
904
- } catch (error) {
905
- return {
906
- _serializationError: "Unable to serialize input",
907
- _type: typeof input,
908
- };
909
- }
910
- }
911
-
912
- /**
913
- * Serialize output for storage
914
- */
915
- serializeOutput(output) {
916
- try {
917
- return JSON.parse(JSON.stringify(output));
918
- } catch (error) {
919
- return {
920
- _serializationError: "Unable to serialize output",
921
- _type: typeof output,
922
- };
923
- }
924
- }
925
-
926
- /**
927
- * Get all profiles
928
- */
929
- getAllProfiles() {
930
- return Array.from(this.profiles.values());
931
- }
932
-
933
- /**
934
- * Get profile by ID
935
- */
936
- getProfile(sessionId) {
937
- return this.profiles.get(sessionId);
938
- }
939
-
940
- /**
941
- * Clear old profiles
942
- */
943
- clearProfiles(olderThanMs = 24 * 60 * 60 * 1000) {
944
- // 24 hours default
945
- const cutoffTime = Date.now() - olderThanMs;
946
-
947
- for (const [sessionId, profile] of this.profiles) {
948
- if (profile.startTime < cutoffTime) {
949
- this.profiles.delete(sessionId);
950
- }
951
- }
952
- }
953
-
954
- /**
955
- * Get profiler status
956
- */
957
- getStatus() {
958
- return {
959
- isProfileing: this.isProfileing,
960
- currentProfile: this.currentProfile?.id || null,
961
- totalProfiles: this.profiles.size,
962
- samplesCollected: this.samples.length,
963
- _options: this._options,
964
- };
965
- }
966
- }
967
-
968
- module.exports = PerformanceProfiler;