blaze-performance-tester 3.1.29 → 3.1.31

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
@@ -93,39 +93,81 @@ async function fetchRcaInsights(semanticReport, apiKey) {
93
93
  }
94
94
  const errorSummary = semanticReport.clusters.map((c) => `[Count: ${c.count}] Category: ${c.category} -> Pattern: ${c.template}`).join("\n");
95
95
  try {
96
- const response = await fetch("https://api.openai.com/v1/chat/completions", {
96
+ const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${apiKey}`, {
97
97
  method: "POST",
98
98
  headers: {
99
- "Content-Type": "application/json",
100
- "Authorization": `Bearer ${apiKey}`
99
+ "Content-Type": "application/json"
101
100
  },
102
101
  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
- },
102
+ systemInstruction: {
103
+ parts: [
104
+ {
105
+ text: "You are a principal systems architect and site reliability engineer. Return clean, direct HTML list items (using <li> tags, no raw markdown blocks) summarizing diagnostics."
106
+ }
107
+ ]
108
+ },
109
+ contents: [
109
110
  {
110
111
  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
+ parts: [
113
+ {
114
+ text: `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
115
 
113
116
  Error Log Patterns:
114
117
  ${errorSummary}`
118
+ }
119
+ ]
115
120
  }
116
121
  ],
117
- temperature: 0.2
122
+ generationConfig: {
123
+ temperature: 0.2
124
+ }
118
125
  })
119
126
  });
120
127
  if (!response.ok) {
121
128
  return `<span style="color: #ef4444;">AI Completion API Error: ${response.status} ${response.statusText}</span>`;
122
129
  }
123
130
  const data = await response.json();
124
- return data.choices?.[0]?.message?.content || "<li>Failed to aggregate autonomous analysis parameters.</li>";
131
+ return data.candidates?.[0]?.content?.parts?.[0]?.text || "<li>Failed to aggregate autonomous analysis parameters.</li>";
125
132
  } catch (error) {
126
133
  return `<span style="color: #ef4444;">Failed to resolve external cloud model: ${error.message}</span>`;
127
134
  }
128
135
  }
136
+ async function fetchIncidentNarrative(history, semanticReport, apiKey) {
137
+ const timelineSummary = history.map((h) => `Wave Tier [${h.threads} VUs] -> Throughput: ${h.throughput.toFixed(0)} req/s, Avg Latency: ${h.avgLatency.toFixed(1)}ms, Error Rate: ${(h.errorRate * 100).toFixed(2)}%`).join("\n");
138
+ const logSummary = semanticReport && semanticReport.clusters ? semanticReport.clusters.map((c) => `[Error Count: ${c.count}] Pattern Blueprint: ${c.template}`).join("\n") : "No major software errors logged.";
139
+ try {
140
+ const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${apiKey}`, {
141
+ method: "POST",
142
+ headers: { "Content-Type": "application/json" },
143
+ body: JSON.stringify({
144
+ systemInstruction: {
145
+ parts: [{
146
+ text: "You are an expert technical writer and lead SRE. Write a fluid, highly professional, plain-English narrative story explaining the timeline of how the system degraded during this load test. Synthesize the raw timeline curves and matching log errors together. Return clean, elegant paragraphs (<p> text </p>) using standard HTML inline formatting elements like <strong> where needed. Do not wrap the response in markdown block quotes (like ```html)."
147
+ }]
148
+ },
149
+ contents: [{
150
+ role: "user",
151
+ parts: [{
152
+ text: `Analyze this historical load pipeline timeline and corresponding logs. Write a clear narrative detailing the failure journey as concurrency grew:
153
+
154
+ === Chronological Testing Waves ===
155
+ ${timelineSummary}
156
+
157
+ === Captured Structural Error Blueprints ===
158
+ ${logSummary}`
159
+ }]
160
+ }],
161
+ generationConfig: { temperature: 0.4 }
162
+ })
163
+ });
164
+ if (!response.ok) return `<em>Narrative generator unavailable: ${response.statusText}</em>`;
165
+ const data = await response.json();
166
+ return data.candidates?.[0]?.content?.parts?.[0]?.text || "<em>Failed to construct incident storyline parameters.</em>";
167
+ } catch (e) {
168
+ return `<em>Failed to compile conversational telemetry narrative: ${e.message}</em>`;
169
+ }
170
+ }
129
171
  async function executeTestPipeline(config) {
130
172
  if (!blazeCore || typeof blazeCore.runCorrelatedTransaction !== "function") {
131
173
  console.error("\u274C Error: runCorrelatedTransaction function is missing from the compiled binary bindings.");
@@ -134,13 +176,13 @@ async function executeTestPipeline(config) {
134
176
  const transactionalScenario = [
135
177
  {
136
178
  name: "Fetch Target Anti-Forgery Nonce",
137
- url: "https://httpbin.org/json",
179
+ url: "[https://httpbin.org/json](https://httpbin.org/json)",
138
180
  method: "GET",
139
181
  body: null
140
182
  },
141
183
  {
142
184
  name: "Perform Contextual Post Action",
143
- url: "https://httpbin.org/post?verification=${csrf_token}",
185
+ url: "[https://httpbin.org/post?verification=](https://httpbin.org/post?verification=)${csrf_token}",
144
186
  method: "POST",
145
187
  body: JSON.stringify({
146
188
  data: "performance_payload",
@@ -187,7 +229,7 @@ function parseArguments(args) {
187
229
  let maxThreads = 300;
188
230
  let maxAllowedLatencyMs = 800;
189
231
  let outputHtml = "blaze-dashboard.html";
190
- let aiKey = "";
232
+ let aiKey = process.env.GEMINI_API_KEY || "";
191
233
  const threadsIdx = args.indexOf("--threads");
192
234
  if (threadsIdx !== -1) threads = parseInt(args[threadsIdx + 1], 10);
193
235
  const durationIdx = args.indexOf("--duration");
@@ -371,6 +413,7 @@ async function runIntelligentAgenticStressTest(config) {
371
413
  }
372
414
  let semanticReport = null;
373
415
  let rcaInsights = "";
416
+ let incidentNarrative = "";
374
417
  if (config.clusterLogs) {
375
418
  if (aggregateErrorLogs.length === 0) {
376
419
  aggregateErrorLogs = getFallbackClusterLogs();
@@ -380,6 +423,8 @@ async function runIntelligentAgenticStressTest(config) {
380
423
  if (config.aiKey) {
381
424
  console.log("\u{1F9E0} Fetching autonomous root-cause analysis from model...");
382
425
  rcaInsights = await fetchRcaInsights(semanticReport, config.aiKey);
426
+ console.log("\u{1F4D6} Generating plain-English incident narrative...");
427
+ incidentNarrative = await fetchIncidentNarrative(history, semanticReport, config.aiKey);
383
428
  }
384
429
  }
385
430
  const finalState = history[history.length - 1] || { apdex: 0, errorRate: 0, p95Latency: 0, throughput: 0 };
@@ -406,7 +451,7 @@ async function runIntelligentAgenticStressTest(config) {
406
451
  Object.keys(globalCodeCounts).forEach((code) => {
407
452
  breakdownRates[code] = totalRequestsAccumulator === 0 ? 0 : globalCodeCounts[code] / totalRequestsAccumulator * 100;
408
453
  });
409
- exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx, breakdownRates }, finalSummaryCards, lastRawRequests, rcaInsights);
454
+ exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx, breakdownRates }, finalSummaryCards, lastRawRequests, rcaInsights, incidentNarrative);
410
455
  }
411
456
  async function runStandardStressTest(config) {
412
457
  console.log(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads} | Duration: ${config.durationSec}s]`);
@@ -426,6 +471,7 @@ async function runStandardStressTest(config) {
426
471
  }];
427
472
  let semanticReport = null;
428
473
  let rcaInsights = "";
474
+ let incidentNarrative = "";
429
475
  if (config.clusterLogs) {
430
476
  let errsToProcess = rawErrors;
431
477
  if (errsToProcess.length === 0) errsToProcess = getFallbackClusterLogs();
@@ -434,6 +480,8 @@ async function runStandardStressTest(config) {
434
480
  if (config.aiKey) {
435
481
  console.log("\u{1F9E0} Fetching autonomous root-cause analysis from model...");
436
482
  rcaInsights = await fetchRcaInsights(semanticReport, config.aiKey);
483
+ console.log("\u{1F4D6} Generating plain-English incident narrative...");
484
+ incidentNarrative = await fetchIncidentNarrative(history, semanticReport, config.aiKey);
437
485
  }
438
486
  }
439
487
  const finalSummaryCards = {
@@ -465,7 +513,7 @@ async function runStandardStressTest(config) {
465
513
  total4xx: metrics.c4xx,
466
514
  total5xx: metrics.c5xx,
467
515
  breakdownRates
468
- }, finalSummaryCards, rawRequests, rcaInsights);
516
+ }, finalSummaryCards, rawRequests, rcaInsights, incidentNarrative);
469
517
  }
470
518
  function getFallbackClusterLogs() {
471
519
  const logs = [];
@@ -520,7 +568,7 @@ function processMetricsTelemetry(requests, durationSec) {
520
568
  const avgTlsMs = mathUtils.mean(requests.map((r) => r.tlsTimeMs || 0));
521
569
  const avgTtfbMs = avgDnsMs + avgTcpMs + avgTlsMs + avgLatencyMs * 0.3;
522
570
  let c1xx = 0, c2xx = 0, c3xx = 0, c4xx = 0, c5xx = 0;
523
- const codeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 0, 502: 0, 503: 0, 504: 0 };
571
+ const codeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 502, 503: 0, 504: 0 };
524
572
  requests.forEach((r) => {
525
573
  if (r.statusCode >= 100 && r.statusCode < 200) c1xx++;
526
574
  else if (r.statusCode >= 200 && r.statusCode < 300) c2xx++;
@@ -581,7 +629,7 @@ function generateFinalAgentReport(history, score) {
581
629
  console.log(`\u26A1 Peak Achieved Cluster Velocity : ${peakThroughput.toFixed(0)} req/sec`);
582
630
  console.log("=======================================================\n");
583
631
  }
584
- function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = [], rcaInsights = "") {
632
+ function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = [], rcaInsights = "", incidentNarrative = "") {
585
633
  const labels = history.map((h, i) => `${i + 1}s`);
586
634
  const successRequestsData = history.map((h) => h.successRequests);
587
635
  const failedWorkloadsData = history.map((h) => h.failedRequests);
@@ -602,7 +650,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
602
650
  if (config.clusterLogs) {
603
651
  const content = rcaInsights ? rcaInsights : `
604
652
  <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.
653
+ \u{1F4A1} <strong>Autonomous RCA Available:</strong> Pass the <code>--ai-key &lt;your-key&gt;</code> flag, or configure a <code>GEMINI_API_KEY</code> environment variable in your PowerShell runtime session to dynamically structure error logs here.
606
654
  </div>`;
607
655
  rcaAccordionHtml = `
608
656
  <details class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #8b5cf6; padding: 0; overflow: hidden;" open>
@@ -761,7 +809,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
761
809
  const htmlContent = `<!DOCTYPE html><html lang="en"><head>
762
810
  <meta charset="UTF-8">
763
811
  <title>Blaze Core Performance Report</title>
764
- <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
812
+ <script src="[https://cdn.jsdelivr.net/npm/chart.js](https://cdn.jsdelivr.net/npm/chart.js)"></script>
765
813
  <style>
766
814
  body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0b111e; color: #f8fafc; margin: 0; padding: 2rem; }
767
815
  .container { max-width: 1300px; margin: 0 auto; }
@@ -819,7 +867,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
819
867
  </style></head><body>
820
868
  <div class="container">
821
869
  <div class="header">
822
- <h1>\u{1F525} Blaze Core Performance Analysis</h1>
870
+ <h1>\u{1F525} Blaze Core Performance Report</h1>
823
871
  <div class="meta">Target Script: <code>${config.targetScript}</code></div>
824
872
  </div>
825
873
 
@@ -862,6 +910,14 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
862
910
  <div class="verdict-box">
863
911
  <div class="verdict-title">\u{1F916} AI Stress-Testing Agent Verdict</div>
864
912
  <div class="verdict-text">${verdictReason}</div>
913
+
914
+ <!-- GENERATIVE INCIDENT NARRATIVE INJECTION PANEL -->
915
+ ${incidentNarrative ? `
916
+ <div style="margin-top: 1rem; margin-bottom: 1rem; padding: 1rem; background: rgba(56, 189, 248, 0.06); border-left: 4px solid #38bdf8; border-radius: 4px; font-size: 0.95rem; line-height: 1.6; color: #cbd5e1;">
917
+ <strong style="color: #38bdf8; display: flex; align-items: center; gap: 0.4rem; margin-bottom: 0.5rem; text-transform: uppercase; font-size: 0.8rem; letter-spacing: 0.05em;">\u{1F4D6} Generative Incident Narrative:</strong>
918
+ ${incidentNarrative}
919
+ </div>` : ""}
920
+
865
921
  <div class="verdict-waves">${waveListItems}</div>
866
922
  </div>
867
923
 
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blaze-performance-tester",
3
- "version": "3.1.29",
3
+ "version": "3.1.31",
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",