blaze-performance-tester 3.2.33 → 3.2.34
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 +126 -6
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5867,7 +5867,7 @@ var i18n = {
|
|
|
5867
5867
|
title: "\u0DA7\u0DD9\u0DBD\u0DD2\u0DB8\u0DD9\u0DA7\u0DCA\u200D\u0DBB\u0DD2 \u0D85\u0DB1\u0DD4\u0D9A\u0DD8\u0DAD\u0DD2 \u0DBD\u0DDC\u0D9C\u0DCA",
|
|
5868
5868
|
threads: "\u0DC3\u0DB8\u0D9C\u0DCF\u0DB8\u0DD3\u0DAD\u0DCA\u0DC0\u0DBA (Threads)",
|
|
5869
5869
|
throughput: "\u0DB4\u0DCA\u200D\u0DBB\u0DC0\u0DCF\u0DC4 \u0D85\u0DB1\u0DD4\u0DB4\u0DCF\u0DAD\u0DBA",
|
|
5870
|
-
avgLatency: "\u0DC3\u0DCF\u0DB8\u0DCF\u0DB1\u0DCA\u200D\
|
|
5870
|
+
avgLatency: "\u0DC3\u0DCF\u0DB8\u0DCF\u0DB1\u0DCA\u200D\u0DBA \u0DB4\u0DCA\u200D\u0DBB\u0DB8\u0DCF\u0DAF\u0DBA",
|
|
5871
5871
|
p95: "P95 \u0DB4\u0DCA\u200D\u0DBB\u0DB8\u0DCF\u0DAF\u0DBA",
|
|
5872
5872
|
errors: "\u0DAF\u0DDD\u0DC2 \u0D85\u0DB1\u0DD4\u0DB4\u0DCF\u0DAD\u0DBA",
|
|
5873
5873
|
status: "\u0DAD\u0DAD\u0DCA\u0DC0\u0DBA",
|
|
@@ -5976,6 +5976,34 @@ ${JSON.stringify(metrics, null, 2)}
|
|
|
5976
5976
|
return null;
|
|
5977
5977
|
}
|
|
5978
5978
|
}
|
|
5979
|
+
async function generateWittyAudioBriefing(cards) {
|
|
5980
|
+
const apiKey = process.env.GEMINI_API_KEY;
|
|
5981
|
+
if (!apiKey) {
|
|
5982
|
+
return "Hey Product Manager! Here is your quick performance update. Actually, your Gemini API Key is missing, so I am improvising. Your health score is sitting at " + cards.healthScore + " out of 100. Check the logs, configure your credentials, and let's make it snappy next time!";
|
|
5983
|
+
}
|
|
5984
|
+
console.log("\u{1F9E0} Querying Gemini 2.5 Flash for a Witty PM Executive Briefing...");
|
|
5985
|
+
const prompt = `
|
|
5986
|
+
You are a fast-talking, highly witty, slightly sarcastic AI performance consultant reporting to a very busy Product Manager.
|
|
5987
|
+
Review these metrics:
|
|
5988
|
+
- Blaze Health Score: ${cards.healthScore}/100
|
|
5989
|
+
- Apdex: ${cards.apdex.toFixed(2)}
|
|
5990
|
+
- Throughput: ${cards.throughput.toFixed(1)} req/s
|
|
5991
|
+
- Error Rate: ${(cards.errorRate * 100).toFixed(2)}%
|
|
5992
|
+
- P95 Latency: ${cards.p95Latency.toFixed(1)}ms
|
|
5993
|
+
|
|
5994
|
+
Compose a witty, energetic daily audio update script that takes exactly 30 seconds to read aloud (around 65-75 words maximum). Focus on what the numbers mean for the business or the features. Return ONLY standard plain-text narrative string, free of markdown styling (*, #, bold tags) or technical headers, ready for immediate browser text-to-speech engine streaming. Make it punchy!
|
|
5995
|
+
`;
|
|
5996
|
+
try {
|
|
5997
|
+
const ai = new GoogleGenAI({});
|
|
5998
|
+
const response = await ai.models.generateContent({
|
|
5999
|
+
model: "gemini-2.5-flash",
|
|
6000
|
+
contents: prompt
|
|
6001
|
+
});
|
|
6002
|
+
return response.text?.replace(/["'**#]/g, "").trim() || "Performance matrix evaluated. Status operational.";
|
|
6003
|
+
} catch (error) {
|
|
6004
|
+
return `System alert PM. Your test achieved a health score of ${cards.healthScore} running at ${cards.throughput.toFixed(0)} requests per second. Check dashboard details for full diagnostic breakdowns.`;
|
|
6005
|
+
}
|
|
6006
|
+
}
|
|
5979
6007
|
async function generateGeminiRCA(history, finalSummary, errorLogs) {
|
|
5980
6008
|
const apiKey = process.env.GEMINI_API_KEY;
|
|
5981
6009
|
if (!apiKey) {
|
|
@@ -6424,6 +6452,7 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
6424
6452
|
};
|
|
6425
6453
|
generateFinalAgentReport(history, healthScore);
|
|
6426
6454
|
const geminiAnalysisHtml = await generateGeminiRCA(history, finalSummaryCards, aggregateErrorLogs);
|
|
6455
|
+
const wittyBriefingText = await generateWittyAudioBriefing(finalSummaryCards);
|
|
6427
6456
|
if (config.optionValue === "elif") {
|
|
6428
6457
|
const plainEnglishSummary = await generateElifExplanation(finalSummaryCards);
|
|
6429
6458
|
if (plainEnglishSummary) {
|
|
@@ -6443,7 +6472,7 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
6443
6472
|
breakdownRates[code] = totalRequestsAccumulator === 0 ? 0 : globalCodeCounts[code] / totalRequestsAccumulator * 100;
|
|
6444
6473
|
});
|
|
6445
6474
|
const baselineData = loadBaselineData(config.baselinePath);
|
|
6446
|
-
exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx, breakdownRates }, finalSummaryCards, lastRawRequests, geminiAnalysisHtml, baselineData);
|
|
6475
|
+
exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx, breakdownRates }, finalSummaryCards, lastRawRequests, geminiAnalysisHtml, wittyBriefingText, baselineData);
|
|
6447
6476
|
}
|
|
6448
6477
|
async function runStandardStressTest(config) {
|
|
6449
6478
|
console.log(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads} | Duration: ${config.durationSec}s | RampUp: ${config.rampUpSec}s | RampDown: ${config.rampDownSec}s]`);
|
|
@@ -6486,6 +6515,7 @@ async function runStandardStressTest(config) {
|
|
|
6486
6515
|
const isPassed = metrics.apdexScore >= config.targetApdex && metrics.errorRate <= config.targetErrorRate;
|
|
6487
6516
|
const verdictReason = isPassed ? "Static single-wave baseline checks concluded successfully without triggering health boundaries." : "Static verification loop completed with values exceeding defined quality thresholds.";
|
|
6488
6517
|
const geminiAnalysisHtml = await generateGeminiRCA(history, finalSummaryCards, rawErrors.length > 0 ? rawErrors : getFallbackClusterLogs());
|
|
6518
|
+
const wittyBriefingText = await generateWittyAudioBriefing(finalSummaryCards);
|
|
6489
6519
|
if (config.optionValue === "elif") {
|
|
6490
6520
|
const plainEnglishSummary = await generateElifExplanation(finalSummaryCards);
|
|
6491
6521
|
if (plainEnglishSummary) {
|
|
@@ -6511,7 +6541,7 @@ async function runStandardStressTest(config) {
|
|
|
6511
6541
|
total4xx: metrics.c4xx,
|
|
6512
6542
|
total5xx: metrics.c5xx,
|
|
6513
6543
|
breakdownRates
|
|
6514
|
-
}, finalSummaryCards, rawRequests, geminiAnalysisHtml, baselineData);
|
|
6544
|
+
}, finalSummaryCards, rawRequests, geminiAnalysisHtml, wittyBriefingText, baselineData);
|
|
6515
6545
|
}
|
|
6516
6546
|
function getFallbackClusterLogs() {
|
|
6517
6547
|
const logs = [];
|
|
@@ -6576,7 +6606,7 @@ function processMetricsTelemetry(requests, durationSec) {
|
|
|
6576
6606
|
const avgTlsMs = mathUtils.mean(requests.map((r) => r.tlsTimeMs || 0));
|
|
6577
6607
|
const avgTtfbMs = avgDnsMs + avgTcpMs + avgTlsMs + avgLatencyMs * 0.3;
|
|
6578
6608
|
let c1xx = 0, c2xx = 0, c3xx = 0, c4xx = 0, c5xx = 0;
|
|
6579
|
-
const codeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500:
|
|
6609
|
+
const codeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 502, 503: 0, 504: 0 };
|
|
6580
6610
|
[400, 401, 403, 404, 429, 500, 502, 503, 504].forEach((c) => codeCounts[c] = 0);
|
|
6581
6611
|
requests.forEach((r) => {
|
|
6582
6612
|
if (r.statusCode >= 100 && r.statusCode < 200)
|
|
@@ -6644,7 +6674,7 @@ function generateFinalAgentReport(history, score) {
|
|
|
6644
6674
|
console.log(`\u26A1 Peak Achieved Cluster Velocity : ${peakThroughput.toFixed(0)} req/sec`);
|
|
6645
6675
|
console.log("=======================================================\n");
|
|
6646
6676
|
}
|
|
6647
|
-
function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = [], geminiAnalysisHtml = "", baselineData = null) {
|
|
6677
|
+
function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = [], geminiAnalysisHtml = "", wittyBriefingText = "", baselineData = null) {
|
|
6648
6678
|
const dict = i18n[config.locale] || i18n["en-US"];
|
|
6649
6679
|
const dir = dict.rtl ? "rtl" : "ltr";
|
|
6650
6680
|
const formatter = new Intl.DateTimeFormat(config.locale || "en-US", {
|
|
@@ -6851,7 +6881,7 @@ ${formattedTicketLogs}
|
|
|
6851
6881
|
.metric-card { background: #131c2e; border: 1px solid #1e293b; border-radius: 6px; padding: 1.25rem; position: relative; }
|
|
6852
6882
|
.card-title { font-size: 0.75rem; font-weight: 700; color: #64748b; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem; }
|
|
6853
6883
|
.card-value { font-size: 1.6rem; font-weight: 700; color: #ffffff; }
|
|
6854
|
-
.card-value .unit { font-size: 0.9rem; font-weight: 500; color: #94a3b8; margin-inline-start: 0.2rem; }
|
|
6884
|
+
.card-value .unit { font-size: 0.9rem; font-weight: 500; color: #94a3b8; margin-inline-start: 0.2rem; display: inline-block; }
|
|
6855
6885
|
.card-value.green { color: #10b981; }
|
|
6856
6886
|
.card-value.orange { color: #f97316; }
|
|
6857
6887
|
.card-value.purple { color: #a855f7; }
|
|
@@ -6905,6 +6935,28 @@ ${formattedTicketLogs}
|
|
|
6905
6935
|
<h1>\u{1F525} ${dict.title}</h1>
|
|
6906
6936
|
<div class="meta">${dict.targetScript}: <code>${config.targetScript}</code> | ${dict.rampUp}: ${config.rampUpSec}s | ${dict.rampDown}: ${config.rampDownSec}s | ${dict.generatedAt}: ${formattedDate}</div>
|
|
6907
6937
|
</div>
|
|
6938
|
+
|
|
6939
|
+
<!-- \u{1F399}\uFE0F Automated Executive Summary Audio Player Dashboard Widget Component -->
|
|
6940
|
+
<div class="card" style="margin-bottom: 2rem; background: #1e1b4b; border: 1px solid #6366f1; display: flex; align-items: center; gap: 1.5rem; padding: 1rem 1.5rem; border-radius: 8px;">
|
|
6941
|
+
<div style="background: #312e81; border-radius: 50%; width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; cursor: pointer; border: 2px solid #818cf8; transition: transform 0.2s; flex-shrink: 0;" id="audioPlayBtn" onclick="toggleBriefingAudio()">
|
|
6942
|
+
<svg id="playIcon" width="18" height="18" viewBox="0 0 24 24" fill="#818cf8"><path d="M8 5v14l11-7z"/></svg>
|
|
6943
|
+
<svg id="pauseIcon" width="18" height="18" viewBox="0 0 24 24" fill="#818cf8" style="display:none;"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>
|
|
6944
|
+
</div>
|
|
6945
|
+
<div style="flex-grow: 1; min-width: 0;">
|
|
6946
|
+
<div style="font-size: 0.85rem; font-weight: bold; color: #818cf8; text-transform: uppercase; letter-spacing: 0.05em; display: flex; align-items: center; gap: 0.5rem;">
|
|
6947
|
+
<span>\u{1F399}\uFE0F Executive Summary Briefing</span>
|
|
6948
|
+
<span id="audioStatus" style="font-size: 0.75rem; color: #94a3b8; text-transform: none; font-weight: normal;">(Ready to play)</span>
|
|
6949
|
+
</div>
|
|
6950
|
+
<div style="font-size: 0.9rem; color: #cbd5e1; font-style: italic; margin-top: 0.25rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;" id="briefingTextPreview"></div>
|
|
6951
|
+
</div>
|
|
6952
|
+
<div style="display: flex; align-items: flex-end; gap: 3px; height: 20px; flex-shrink: 0;" id="visualizerBars">
|
|
6953
|
+
<div style="width: 3px; height: 8px; background: #6366f1; border-radius: 1px; transition: height 0.15s ease;"></div>
|
|
6954
|
+
<div style="width: 3px; height: 14px; background: #6366f1; border-radius: 1px; transition: height 0.15s ease;"></div>
|
|
6955
|
+
<div style="width: 3px; height: 6px; background: #6366f1; border-radius: 1px; transition: height 0.15s ease;"></div>
|
|
6956
|
+
<div style="width: 3px; height: 18px; background: #6366f1; border-radius: 1px; transition: height 0.15s ease;"></div>
|
|
6957
|
+
<div style="width: 3px; height: 10px; background: #6366f1; border-radius: 1px; transition: height 0.15s ease;"></div>
|
|
6958
|
+
</div>
|
|
6959
|
+
</div>
|
|
6908
6960
|
|
|
6909
6961
|
<div class="cards-wrapper">
|
|
6910
6962
|
<div class="metric-card">
|
|
@@ -7086,6 +7138,74 @@ ${formattedTicketLogs}
|
|
|
7086
7138
|
</div>
|
|
7087
7139
|
|
|
7088
7140
|
<script>
|
|
7141
|
+
// Inject the generated Briefing Text payload
|
|
7142
|
+
const textBriefingPayload = ${JSON.stringify(wittyBriefingText)};
|
|
7143
|
+
document.getElementById('briefingTextPreview').textContent = '"' + textBriefingPayload + '"';
|
|
7144
|
+
|
|
7145
|
+
let synthesisEngine = window.speechSynthesis;
|
|
7146
|
+
let textUtterance = null;
|
|
7147
|
+
let audioStateActive = false;
|
|
7148
|
+
let visualBarsTimer = null;
|
|
7149
|
+
|
|
7150
|
+
function toggleBriefingAudio() {
|
|
7151
|
+
if (!synthesisEngine) {
|
|
7152
|
+
alert("Speech synthesis metrics streaming unsupported on this browser platform.");
|
|
7153
|
+
return;
|
|
7154
|
+
}
|
|
7155
|
+
|
|
7156
|
+
if (audioStateActive) {
|
|
7157
|
+
synthesisEngine.cancel();
|
|
7158
|
+
terminateAudioVisuals();
|
|
7159
|
+
} else {
|
|
7160
|
+
synthesisEngine.cancel();
|
|
7161
|
+
textUtterance = new SpeechSynthesisUtterance(textBriefingPayload);
|
|
7162
|
+
|
|
7163
|
+
// Match specialized high quality voice parameters if dynamically present
|
|
7164
|
+
let targetVoices = synthesisEngine.getVoices();
|
|
7165
|
+
let activeVoice = targetVoices.find(v => v.lang.startsWith('en') && v.name.includes('Google')) || targetVoices.find(v => v.lang.startsWith('en'));
|
|
7166
|
+
if (activeVoice) textUtterance.voice = activeVoice;
|
|
7167
|
+
|
|
7168
|
+
textUtterance.rate = 1.08; // Snappy presentation delivery mapping
|
|
7169
|
+
textUtterance.pitch = 1.0;
|
|
7170
|
+
|
|
7171
|
+
textUtterance.onend = () => terminateAudioVisuals();
|
|
7172
|
+
textUtterance.onerror = () => terminateAudioVisuals();
|
|
7173
|
+
|
|
7174
|
+
synthesisEngine.speak(textUtterance);
|
|
7175
|
+
initializeAudioVisuals();
|
|
7176
|
+
}
|
|
7177
|
+
}
|
|
7178
|
+
|
|
7179
|
+
function initializeAudioVisuals() {
|
|
7180
|
+
audioStateActive = true;
|
|
7181
|
+
document.getElementById('playIcon').style.display = 'none';
|
|
7182
|
+
document.getElementById('pauseIcon').style.display = 'block';
|
|
7183
|
+
document.getElementById('audioStatus').textContent = '(Briefing playing...)';
|
|
7184
|
+
document.getElementById('audioPlayBtn').style.transform = 'scale(1.05)';
|
|
7185
|
+
|
|
7186
|
+
const bars = document.getElementById('visualizerBars').children;
|
|
7187
|
+
visualBarsTimer = setInterval(() => {
|
|
7188
|
+
for (let bar of bars) {
|
|
7189
|
+
bar.style.height = (Math.floor(Math.random() * 16) + 4) + 'px';
|
|
7190
|
+
}
|
|
7191
|
+
}, 120);
|
|
7192
|
+
}
|
|
7193
|
+
|
|
7194
|
+
function terminateAudioVisuals() {
|
|
7195
|
+
audioStateActive = false;
|
|
7196
|
+
document.getElementById('playIcon').style.display = 'block';
|
|
7197
|
+
document.getElementById('pauseIcon').style.display = 'none';
|
|
7198
|
+
document.getElementById('audioStatus').textContent = '(Paused / Finished)';
|
|
7199
|
+
document.getElementById('audioPlayBtn').style.transform = 'scale(1)';
|
|
7200
|
+
clearInterval(visualBarsTimer);
|
|
7201
|
+
|
|
7202
|
+
const defaultHeights = [8, 14, 6, 18, 10];
|
|
7203
|
+
const bars = document.getElementById('visualizerBars').children;
|
|
7204
|
+
for (let i = 0; i < bars.length; i++) {
|
|
7205
|
+
if(bars[i]) bars[i].style.height = defaultHeights[i] + 'px';
|
|
7206
|
+
}
|
|
7207
|
+
}
|
|
7208
|
+
|
|
7089
7209
|
// Inject and expose the formatted Fix-It Ticket Markdown
|
|
7090
7210
|
const rawTicketMarkdown = ${JSON.stringify(generatedTicketMarkdown)};
|
|
7091
7211
|
document.getElementById('ticketBodyPreview').textContent = rawTicketMarkdown;
|