blaze-performance-tester 3.1.27 → 3.1.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -87,6 +87,45 @@ var mathUtils = {
87
87
  return sorted[Math.max(0, index)];
88
88
  }
89
89
  };
90
+ async function fetchRcaInsights(semanticReport, apiKey) {
91
+ if (!semanticReport || !semanticReport.clusters || semanticReport.clusters.length === 0) {
92
+ return `<ul><li><strong>Nominal Operational Bounds:</strong> No cluster-level anomalies or processing faults were registered during this execution frame.</li></ul>`;
93
+ }
94
+ const errorSummary = semanticReport.clusters.map((c) => `[Count: ${c.count}] Category: ${c.category} -> Pattern: ${c.template}`).join("\n");
95
+ try {
96
+ const response = await fetch("https://api.openai.com/v1/chat/completions", {
97
+ method: "POST",
98
+ headers: {
99
+ "Content-Type": "application/json",
100
+ "Authorization": `Bearer ${apiKey}`
101
+ },
102
+ body: JSON.stringify({
103
+ model: "gpt-4o-mini",
104
+ messages: [
105
+ {
106
+ role: "system",
107
+ content: "You are a principal systems architect and site reliability engineer. Return clean, direct HTML list items (\u7528 <li> tags, no raw markdown blocks) summarizing diagnostics."
108
+ },
109
+ {
110
+ role: "user",
111
+ content: `Analyze these performance test errors and provide 3 bullet points detailing the likely backend bottleneck (e.g., database connection pool exhaustion) and practical configuration fixes.
112
+
113
+ Error Log Patterns:
114
+ ${errorSummary}`
115
+ }
116
+ ],
117
+ temperature: 0.2
118
+ })
119
+ });
120
+ if (!response.ok) {
121
+ return `<span style="color: #ef4444;">AI Completion API Error: ${response.status} ${response.statusText}</span>`;
122
+ }
123
+ const data = await response.json();
124
+ return data.choices?.[0]?.message?.content || "<li>Failed to aggregate autonomous analysis parameters.</li>";
125
+ } catch (error) {
126
+ return `<span style="color: #ef4444;">Failed to resolve external cloud model: ${error.message}</span>`;
127
+ }
128
+ }
90
129
  async function executeTestPipeline(config) {
91
130
  if (!blazeCore || typeof blazeCore.runCorrelatedTransaction !== "function") {
92
131
  console.error("\u274C Error: runCorrelatedTransaction function is missing from the compiled binary bindings.");
@@ -148,6 +187,7 @@ function parseArguments(args) {
148
187
  let maxThreads = 300;
149
188
  let maxAllowedLatencyMs = 800;
150
189
  let outputHtml = "blaze-dashboard.html";
190
+ let aiKey = "";
151
191
  const threadsIdx = args.indexOf("--threads");
152
192
  if (threadsIdx !== -1) threads = parseInt(args[threadsIdx + 1], 10);
153
193
  const durationIdx = args.indexOf("--duration");
@@ -160,6 +200,8 @@ function parseArguments(args) {
160
200
  if (maxThreadsIdx !== -1) maxThreads = parseInt(args[maxThreadsIdx + 1], 10);
161
201
  const outputIdx = args.indexOf("--output");
162
202
  if (outputIdx !== -1) outputHtml = args[outputIdx + 1];
203
+ const aiKeyIdx = args.indexOf("--ai-key");
204
+ if (aiKeyIdx !== -1) aiKey = args[aiKeyIdx + 1];
163
205
  const positionalNums = args.slice(1).filter((arg) => !arg.startsWith("-")).map(Number).filter((n) => !isNaN(n));
164
206
  if (positionalNums.length > 0 && threadsIdx === -1 && !isAgentic) threads = positionalNums[0];
165
207
  if (positionalNums.length > 1 && durationIdx === -1 && !isAgentic) durationSec = positionalNums[1];
@@ -191,7 +233,8 @@ function parseArguments(args) {
191
233
  maxThreads,
192
234
  stepSize: 15,
193
235
  maxAllowedLatencyMs,
194
- outputHtml
236
+ outputHtml,
237
+ aiKey
195
238
  };
196
239
  }
197
240
  async function runBlazeCoreEngine(config) {
@@ -327,12 +370,17 @@ async function runIntelligentAgenticStressTest(config) {
327
370
  currentThreads += nextStep;
328
371
  }
329
372
  let semanticReport = null;
373
+ let rcaInsights = "";
330
374
  if (config.clusterLogs) {
331
375
  if (aggregateErrorLogs.length === 0) {
332
376
  aggregateErrorLogs = getFallbackClusterLogs();
333
377
  }
334
378
  const analyzer = new LogAnalyzer();
335
379
  semanticReport = analyzer.process(aggregateErrorLogs);
380
+ if (config.aiKey) {
381
+ console.log("\u{1F9E0} Fetching autonomous root-cause analysis from model...");
382
+ rcaInsights = await fetchRcaInsights(semanticReport, config.aiKey);
383
+ }
336
384
  }
337
385
  const finalState = history[history.length - 1] || { apdex: 0, errorRate: 0, p95Latency: 0, throughput: 0 };
338
386
  const healthScore = computeHealthScoreValue(finalState.apdex, finalState.errorRate, finalState.p95Latency, config.maxAllowedLatencyMs);
@@ -358,7 +406,7 @@ async function runIntelligentAgenticStressTest(config) {
358
406
  Object.keys(globalCodeCounts).forEach((code) => {
359
407
  breakdownRates[code] = totalRequestsAccumulator === 0 ? 0 : globalCodeCounts[code] / totalRequestsAccumulator * 100;
360
408
  });
361
- exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx, breakdownRates }, finalSummaryCards, lastRawRequests);
409
+ exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx, breakdownRates }, finalSummaryCards, lastRawRequests, rcaInsights);
362
410
  }
363
411
  async function runStandardStressTest(config) {
364
412
  console.log(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads} | Duration: ${config.durationSec}s]`);
@@ -377,11 +425,16 @@ async function runStandardStressTest(config) {
377
425
  failedRequests: metrics.c4xx + metrics.c5xx
378
426
  }];
379
427
  let semanticReport = null;
428
+ let rcaInsights = "";
380
429
  if (config.clusterLogs) {
381
430
  let errsToProcess = rawErrors;
382
431
  if (errsToProcess.length === 0) errsToProcess = getFallbackClusterLogs();
383
432
  const analyzer = new LogAnalyzer();
384
433
  semanticReport = analyzer.process(errsToProcess);
434
+ if (config.aiKey) {
435
+ console.log("\u{1F9E0} Fetching autonomous root-cause analysis from model...");
436
+ rcaInsights = await fetchRcaInsights(semanticReport, config.aiKey);
437
+ }
385
438
  }
386
439
  const finalSummaryCards = {
387
440
  apdex: metrics.apdexScore,
@@ -412,7 +465,7 @@ async function runStandardStressTest(config) {
412
465
  total4xx: metrics.c4xx,
413
466
  total5xx: metrics.c5xx,
414
467
  breakdownRates
415
- }, finalSummaryCards, rawRequests);
468
+ }, finalSummaryCards, rawRequests, rcaInsights);
416
469
  }
417
470
  function getFallbackClusterLogs() {
418
471
  const logs = [];
@@ -467,7 +520,7 @@ function processMetricsTelemetry(requests, durationSec) {
467
520
  const avgTlsMs = mathUtils.mean(requests.map((r) => r.tlsTimeMs || 0));
468
521
  const avgTtfbMs = avgDnsMs + avgTcpMs + avgTlsMs + avgLatencyMs * 0.3;
469
522
  let c1xx = 0, c2xx = 0, c3xx = 0, c4xx = 0, c5xx = 0;
470
- const codeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 502, 502: 0, 503: 0, 504: 0 };
523
+ const codeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 0, 502: 0, 503: 0, 504: 0 };
471
524
  requests.forEach((r) => {
472
525
  if (r.statusCode >= 100 && r.statusCode < 200) c1xx++;
473
526
  else if (r.statusCode >= 200 && r.statusCode < 300) c2xx++;
@@ -528,7 +581,7 @@ function generateFinalAgentReport(history, score) {
528
581
  console.log(`\u26A1 Peak Achieved Cluster Velocity : ${peakThroughput.toFixed(0)} req/sec`);
529
582
  console.log("=======================================================\n");
530
583
  }
531
- function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = []) {
584
+ function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = [], rcaInsights = "") {
532
585
  const labels = history.map((h, i) => `${i + 1}s`);
533
586
  const successRequestsData = history.map((h) => h.successRequests);
534
587
  const failedWorkloadsData = history.map((h) => h.failedRequests);
@@ -545,6 +598,26 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
545
598
  x: Number(((r.timestampMs - firstTimestamp) / 1e3).toFixed(2)),
546
599
  y: Number(((r.dnsTimeMs || 0) + (r.tcpTimeMs || 0) + (r.tlsTimeMs || 0) + r.durationMs * 0.3).toFixed(1))
547
600
  }));
601
+ let rcaAccordionHtml = "";
602
+ if (config.clusterLogs) {
603
+ const content = rcaInsights ? rcaInsights : `
604
+ <div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px dashed #475569; color: #94a3b8;">
605
+ \u{1F4A1} <strong>Autonomous RCA Available:</strong> Pass the <code>--ai-key &lt;your-key&gt;</code> flag during pipeline execution to automatically turn structural failure logs into actionable configuration mitigations here.
606
+ </div>`;
607
+ rcaAccordionHtml = `
608
+ <details class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #8b5cf6; padding: 0; overflow: hidden;" open>
609
+ <summary style="font-size: 1.2rem; font-weight: bold; color: #c084fc; padding: 1.25rem; display: flex; align-items: center; justify-content: space-between; cursor: pointer; outline: none; user-select: none; list-style: none;">
610
+ <span style="display: flex; align-items: center; gap: 0.6rem;">\u{1F9E0} Autonomous Root-Cause Analysis (RCA) Insights</span>
611
+ <span style="font-size: 0.8rem; color: #64748b; font-weight: normal; font-family: monospace;">[CLICK TO COLLAPSE/EXPAND]</span>
612
+ </summary>
613
+ <div style="padding: 0 1.25rem 1.25rem 1.25rem; border-top: 1px solid #1e293b; color: #cbd5e1; font-size: 0.95rem; line-height: 1.6;">
614
+ <div style="margin-top: 1rem; margin-bottom: 0.5rem; color: #a78bfa; font-weight: 600;">System Diagnostic Summary:</div>
615
+ <div style="padding-left: 0.5rem;">
616
+ ${content}
617
+ </div>
618
+ </div>
619
+ </details>`;
620
+ }
548
621
  let logClustersHtml = "";
549
622
  if (semanticReport) {
550
623
  const totalLogCount = semanticReport.clusters.reduce((sum, c) => sum + c.count, 0);
@@ -552,7 +625,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
552
625
  logClustersHtml = `
553
626
  <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
554
627
  <h3 style="border-bottom: none; padding-bottom: 0rem; margin-bottom: 0.25rem; display: flex; align-items: center; gap: 0.5rem; color: #ffffff; font-size: 1.2rem;">
555
- \u{1F9E0} Semantic Log Clustering & Error Classification
628
+ \u{1F4CA} Semantic Log Clustering & Error Classification
556
629
  </h3>
557
630
  <div style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
558
631
  Captured <strong style="color: #f8fafc;">${totalLogCount}</strong> raw failures grouped into <strong style="color: #f8fafc;">${uniquePatternsCount}</strong> unique structural patterns.
@@ -741,6 +814,8 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
741
814
  .badge { padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.8rem; font-weight: bold; }
742
815
  .badge-success { background: #16a34a; color: #f0fdf4; }
743
816
  .badge-fail { background: #dc2626; color: #fef2f2; }
817
+
818
+ summary::-webkit-details-marker { display: none; }
744
819
  </style></head><body>
745
820
  <div class="container">
746
821
  <div class="header">
@@ -790,7 +865,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
790
865
  <div class="verdict-waves">${waveListItems}</div>
791
866
  </div>
792
867
 
793
- <!-- IMPLEMENTED CARD FEATURE 76: Executive Health Gauges Panel -->
794
868
  <div class="gauge-container-box">
795
869
  <div class="gauge-title">Blaze Run Health Score</div>
796
870
  <div style="position: relative; width: 140px; height: 140px; display: flex; align-items: center; justify-content: center;">
@@ -851,7 +925,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
851
925
  </div>
852
926
 
853
927
  <div class="graph-card">
854
- <div class="graph-title">\u{1F504} Active Concurrency (VUs) vs. TTF Trend</div>
928
+ <div class="graph-title">\u{1F504} Active Concurrency (VUs) vs. TTF Trend & AI Load Forecast</div>
855
929
  <div style="height: 280px; position: relative;">
856
930
  <canvas id="concurrencyChart"></canvas>
857
931
  </div>
@@ -903,6 +977,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
903
977
  ${seoImpactSectionHtml}
904
978
  ${liveAdjusterHtml}
905
979
  ${rightSizingHtml}
980
+ ${rcaAccordionHtml}
906
981
  ${logClustersHtml}
907
982
  </div>
908
983
 
@@ -960,14 +1035,52 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
960
1035
  }
961
1036
  });
962
1037
 
1038
+ const baseActiveThreads = ${JSON.stringify(activeThreadsData)};
1039
+ const baseTtfTrend = ${JSON.stringify(ttfTrendData)};
1040
+ const historicalCount = baseTtfTrend.length;
1041
+
1042
+ let extendedLabels = [...labels];
1043
+ let extendedThreads = [...baseActiveThreads];
1044
+ let extendedTtf = [...baseTtfTrend];
1045
+ let aiForecastData = [];
1046
+
1047
+ if (historicalCount >= 2) {
1048
+ let sumX = 0, sumY = 0, sumXY = 0, sumXX = 0;
1049
+ for (let i = 0; i < historicalCount; i++) {
1050
+ sumX += i;
1051
+ sumY += baseTtfTrend[i];
1052
+ sumXY += i * baseTtfTrend[i];
1053
+ sumXX += i * i;
1054
+ }
1055
+ const denominator = (historicalCount * sumXX - sumX * sumX);
1056
+ const slope = denominator === 0 ? 0 : (historicalCount * sumXY - sumX * sumY) / denominator;
1057
+ const intercept = (sumY - slope * sumX) / historicalCount;
1058
+
1059
+ for (let i = 0; i < historicalCount; i++) {
1060
+ aiForecastData.push(Number((slope * i + intercept).toFixed(1)));
1061
+ }
1062
+
1063
+ const lastThreads = baseActiveThreads[historicalCount - 1] || 10;
1064
+ const threadStep = ${config.stepSize || 15};
1065
+
1066
+ for (let i = 1; i <= 3; i++) {
1067
+ extendedLabels.push(\`+\${i * 5}s (AI Forecast)\`);
1068
+ extendedThreads.push(lastThreads + (threadStep * i));
1069
+ extendedTtf.push(null);
1070
+ aiForecastData.push(Number((slope * (historicalCount + i - 1) + intercept).toFixed(1)));
1071
+ }
1072
+ } else {
1073
+ aiForecastData = [...baseTtfTrend];
1074
+ }
1075
+
963
1076
  new Chart(document.getElementById('concurrencyChart'), {
964
1077
  type: 'line',
965
1078
  data: {
966
- labels: labels,
1079
+ labels: extendedLabels,
967
1080
  datasets: [
968
1081
  {
969
1082
  label: 'Active VU Threads',
970
- data: ${JSON.stringify(activeThreadsData)},
1083
+ data: extendedThreads,
971
1084
  borderColor: '#38bdf8',
972
1085
  backgroundColor: 'rgba(56, 189, 248, 0.1)',
973
1086
  borderWidth: 2.5,
@@ -977,13 +1090,24 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
977
1090
  },
978
1091
  {
979
1092
  label: 'Time-to-Failure (TTF) Trend',
980
- data: ${JSON.stringify(ttfTrendData)},
1093
+ data: extendedTtf,
981
1094
  borderColor: '#f43f5e',
982
1095
  borderWidth: 2,
983
1096
  borderDash: [5, 5],
984
1097
  pointRadius: 3,
985
1098
  fill: false,
986
1099
  yAxisID: 'yTtf'
1100
+ },
1101
+ {
1102
+ label: '\u{1F52E} AI Capacity Forecast Line',
1103
+ data: aiForecastData,
1104
+ borderColor: '#a855f7',
1105
+ borderWidth: 2,
1106
+ borderDash: [3, 3],
1107
+ pointRadius: 4,
1108
+ pointBackgroundColor: '#a855f7',
1109
+ fill: false,
1110
+ yAxisID: 'yTtf'
987
1111
  }
988
1112
  ]
989
1113
  },
@@ -1056,6 +1180,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
1056
1180
  function printUsage() {
1057
1181
  console.log(`Blaze Core Performance Test CLI Engine
1058
1182
  Options:
1059
- --threads <count> | --duration <seconds> | --agentic | --cluster-logs | --correlate | --seo`);
1183
+ --threads <count> | --duration <seconds> | --agentic | --cluster-logs | --correlate | --seo | --ai-key <api-key>`);
1060
1184
  }
1061
1185
  main().catch(console.error);
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blaze-performance-tester",
3
- "version": "3.1.27",
3
+ "version": "3.1.29",
4
4
  "description": "A high-performance, multi-threaded load testing engine built with Rust and QuickJS.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",