Improvements in tool call stats display

This commit is contained in:
Michael Panchenko authored and Michael Panchenko committed 2025-07-12 20:54:06 +02:00
1 parent 56902e58e2
commit 3c0f33acde
2 files changed
+237 -13

No files matched your search

+147 -7
View File
@@ -42,9 +42,11 @@ class Dashboard {
this.$shutdownButton = $('#shutdown');
this.$toggleStats = $('#toggle-stats');
this.$statsSection = $('#stats-section');
this.$refreshStats = $('#refresh-stats');
this.$clearStats = $('#clear-stats');
this.countChart = null;
this.tokensChart = null;
this.inputChart = null;
this.outputChart = null;
@@ -52,6 +54,7 @@ class Dashboard {
this.$loadButton.click(this.loadLogs.bind(this));
this.$shutdownButton.click(this.shutdown.bind(this));
this.$toggleStats.click(this.toggleStats.bind(this));
this.$refreshStats.click(this.loadStats.bind(this));
this.$clearStats.click(this.clearStats.bind(this));
// initialize the application
@@ -229,41 +232,178 @@ class Dashboard {
const counts = names.map(n => stats[n].num_times_called);
const inputTokens = names.map(n => stats[n].input_tokens);
const outputTokens = names.map(n => stats[n].output_tokens);
const totalTokens = names.map(n => stats[n].input_tokens + stats[n].output_tokens);
// Calculate totals for summary table
const totalCalls = counts.reduce((sum, count) => sum + count, 0);
const totalInputTokens = inputTokens.reduce((sum, tokens) => sum + tokens, 0);
const totalOutputTokens = outputTokens.reduce((sum, tokens) => sum + tokens, 0);
// Generate consistent colors for tools
const colors = this.generateColors(names.length);
const countCtx = document.getElementById('count-chart');
const tokensCtx = document.getElementById('tokens-chart');
const inputCtx = document.getElementById('input-chart');
const outputCtx = document.getElementById('output-chart');
if (this.countChart) this.countChart.destroy();
if (this.tokensChart) this.tokensChart.destroy();
if (this.inputChart) this.inputChart.destroy();
if (this.outputChart) this.outputChart.destroy();
if (names.length === 0) {
this.countChart = null;
this.tokensChart = null;
this.inputChart = null;
this.outputChart = null;
countCtx.getContext('2d').clearRect(0,0,countCtx.width,countCtx.height);
tokensCtx.getContext('2d').clearRect(0,0,tokensCtx.width,tokensCtx.height);
inputCtx.getContext('2d').clearRect(0,0,inputCtx.width,inputCtx.height);
outputCtx.getContext('2d').clearRect(0,0,outputCtx.width,outputCtx.height);
return;
}
// Update summary table
this.updateSummaryTable(totalCalls, totalInputTokens, totalOutputTokens);
// Register datalabels plugin
Chart.register(ChartDataLabels);
// Tool calls pie chart
this.countChart = new Chart(countCtx, {
type: 'pie',
data: { labels: names, datasets: [{ data: counts }] },
data: {
labels: names,
datasets: [{
data: counts,
backgroundColor: colors
}]
},
options: {
plugins: {
legend: { display: true },
datalabels: {
display: true,
color: 'white',
font: { weight: 'bold' },
formatter: (value) => value
}
}
}
});
// Input tokens pie chart
this.inputChart = new Chart(inputCtx, {
type: 'bar',
data: { labels: names, datasets: [{ label: 'Input Tokens', data: inputTokens }] },
options: { scales: { y: { beginAtZero: true } } }
type: 'pie',
data: {
labels: names,
datasets: [{
data: inputTokens,
backgroundColor: colors
}]
},
options: {
plugins: {
legend: { display: true },
datalabels: {
display: true,
color: 'white',
font: { weight: 'bold' },
formatter: (value) => value
}
}
}
});
// Output tokens pie chart
this.outputChart = new Chart(outputCtx, {
type: 'bar',
data: { labels: names, datasets: [{ label: 'Output Tokens', data: outputTokens }] },
options: { scales: { y: { beginAtZero: true } } }
type: 'pie',
data: {
labels: names,
datasets: [{
data: outputTokens,
backgroundColor: colors
}]
},
options: {
plugins: {
legend: { display: true },
datalabels: {
display: true,
color: 'white',
font: { weight: 'bold' },
formatter: (value) => value
}
}
}
});
// Combined input/output tokens bar chart
this.tokensChart = new Chart(tokensCtx, {
type: 'bar',
data: {
labels: names,
datasets: [
{
label: 'Input Tokens',
data: inputTokens,
backgroundColor: colors.map(color => color + '80'), // Semi-transparent
borderColor: colors,
borderWidth: 2,
borderSkipped: false,
yAxisID: 'y'
},
{
label: 'Output Tokens',
data: outputTokens,
backgroundColor: colors,
yAxisID: 'y1'
}
]
},
options: {
responsive: true,
scales: {
y: {
type: 'linear',
display: true,
position: 'left',
beginAtZero: true,
title: { display: true, text: 'Input Tokens' }
},
y1: {
type: 'linear',
display: true,
position: 'right',
beginAtZero: true,
title: { display: true, text: 'Output Tokens' },
grid: { drawOnChartArea: false }
}
}
}
});
}
generateColors(count) {
const colors = [
'#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF',
'#FF9F40', '#FF6384', '#C9CBCF', '#4BC0C0', '#FF6384'
];
return Array.from({length: count}, (_, i) => colors[i % colors.length]);
}
updateSummaryTable(totalCalls, totalInputTokens, totalOutputTokens) {
const tableHtml = `
<table class="stats-summary">
<tr><th>Metric</th><th>Total</th></tr>
<tr><td>Tool Calls</td><td>${totalCalls}</td></tr>
<tr><td>Input Tokens</td><td>${totalInputTokens}</td></tr>
<tr><td>Output Tokens</td><td>${totalOutputTokens}</td></tr>
<tr><td>Total Tokens</td><td>${totalInputTokens + totalOutputTokens}</td></tr>
</table>
`;
$('#stats-summary').html(tableHtml);
}
shutdown() {
+90 -6
View File
@@ -9,6 +9,7 @@
<link rel="icon" type="image/png" sizes="48x48" href="serena-icon-48.png">
<script src="jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2"></script>
<script src="dashboard.js"></script>
<style>
body {
@@ -98,6 +99,70 @@
text-align: center;
margin: 10px 0;
}
.charts-container {
display: flex;
flex-wrap: wrap;
gap: 15px;
justify-content: space-between;
max-width: 1400px;
margin: 0 auto;
}
.chart-group {
flex: 1;
min-width: 280px;
max-width: 320px;
text-align: center;
}
.chart-wide {
flex: 0 0 100%;
min-width: 100%;
margin-top: 10px;
}
.chart-group h3 {
margin: 0 0 10px 0;
color: #333;
}
.stats-summary {
margin: 0 auto;
border-collapse: collapse;
background: white;
border-radius: 5px;
overflow: hidden;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.stats-summary th,
.stats-summary td {
padding: 10px 20px;
text-align: left;
border-bottom: 1px solid #ddd;
}
.stats-summary th {
background-color: #f8f9fa;
font-weight: bold;
}
.stats-summary tr:last-child td {
border-bottom: none;
}
@media (max-width: 768px) {
.charts-container {
flex-direction: column;
}
.chart-group,
.chart-wide {
min-width: auto;
max-width: none;
}
}
</style>
</head>
<body>
@@ -114,12 +179,31 @@
<div id="error-container"></div>
<div id="log-container" class="log-container"></div>
<div id="stats-section" style="display:none; margin-top:20px; text-align:center;">
<button id="clear-stats" class="btn" style="margin-bottom:10px;">Clear Stats</button>
<div style="max-width:600px;margin:0 auto;">
<canvas id="count-chart" height="200"></canvas>
<canvas id="input-chart" height="200"></canvas>
<canvas id="output-chart" height="200"></canvas>
<div id="stats-section" style="display:none; margin-top:20px;">
<div style="text-align:center; margin-bottom:20px;">
<button id="refresh-stats" class="btn">Refresh Stats</button>
<button id="clear-stats" class="btn">Clear Stats</button>
</div>
<div id="stats-summary" style="margin-bottom:20px; text-align:center;"></div>
<div class="charts-container">
<div class="chart-group">
<h3>Tool Calls</h3>
<canvas id="count-chart" height="200"></canvas>
</div>
<div class="chart-group">
<h3>Input Tokens</h3>
<canvas id="input-chart" height="200"></canvas>
</div>
<div class="chart-group">
<h3>Output Tokens</h3>
<canvas id="output-chart" height="200"></canvas>
</div>
<div class="chart-group chart-wide">
<h3>Input vs Output Tokens</h3>
<canvas id="tokens-chart" height="120"></canvas>
</div>
</div>
</div>