mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-14 00:16:57 +00:00
Improvements in tool call stats display
This commit is contained in:
1 parent
56902e58e2
commit
3c0f33acde
2 files changed
+237
-13
No files matched your search
@@ -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() {
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
Reference in new issue
Block a user