Minor improvements of stats display in dashboard

Display token count estimator name
Display short message if no stats were collected
This commit is contained in:
Michael Panchenko
2025-07-13 19:45:41 +02:00
committed by Michael Panchenko
parent f9609b8d9f
commit 93702f3932
4 changed files with 53 additions and 26 deletions
+11 -3
View File
@@ -2,7 +2,7 @@ from __future__ import annotations
import logging
import threading
from abc import ABC
from abc import ABC, abstractmethod
from collections import defaultdict
from copy import copy
from dataclasses import asdict, dataclass
@@ -15,6 +15,7 @@ log = logging.getLogger(__name__)
class TokenCountEstimator(ABC):
@abstractmethod
def estimate_token_count(self, text: str) -> int:
"""
Estimate the number of tokens in the given text.
@@ -84,9 +85,9 @@ class RegisteredTokenCountEstimator(Enum):
def _create_estimator(self) -> TokenCountEstimator:
match self:
case self.TIKTOKEN_GPT4O:
case RegisteredTokenCountEstimator.TIKTOKEN_GPT4O:
return TiktokenCountEstimator(model_name="gpt-4o")
case self.ANTHROPIC_CLAUDE_SONNET_4:
case RegisteredTokenCountEstimator.ANTHROPIC_CLAUDE_SONNET_4:
return AnthropicTokenCount(model_name="claude-sonnet-4-20250514")
case _:
raise ValueError(f"Unknown token count estimator: {self.value}")
@@ -110,6 +111,13 @@ class ToolUsageStats:
self._tool_stats: dict[str, ToolUsageStats.Entry] = defaultdict(ToolUsageStats.Entry)
self._tool_stats_lock = threading.Lock()
@property
def token_estimator_name(self) -> str:
"""
Get the name of the registered token count estimator used.
"""
return self._token_estimator_name
@dataclass(kw_only=True)
class Entry:
num_times_called: int = 0
+7 -1
View File
@@ -130,6 +130,11 @@ class SerenaDashboardAPI:
self._clear_tool_stats()
return {"status": "cleared"}
@self._app.route("/get_token_count_estimator_name", methods=["GET"])
def get_token_count_estimator_name() -> dict[str, str]:
estimator_name = self._tool_usage_stats.token_estimator_name if self._tool_usage_stats else "unknown"
return {"token_count_estimator_name": estimator_name}
@self._app.route("/shutdown", methods=["PUT"])
def shutdown() -> dict[str, str]:
self._shutdown()
@@ -150,7 +155,8 @@ class SerenaDashboardAPI:
return ResponseToolStats(stats={})
def _clear_tool_stats(self) -> None:
self._tool_usage_stats.clear()
if self._tool_usage_stats is not None:
self._tool_usage_stats.clear()
def _shutdown(self) -> None:
log.info("Shutting down Serena")
+30 -22
View File
@@ -201,18 +201,19 @@ class Dashboard {
loadStats() {
let self = this;
$.ajax({
url: '/get_tool_stats',
type: 'GET',
success: function(response) {
self.displayStats(response.stats || {});
},
error: function(xhr, status, error) {
console.error('Error loading stats:', error);
}
$.when(
$.ajax({ url: '/get_tool_stats', type: 'GET' }),
$.ajax({ url: '/get_token_count_estimator_name', type: 'GET' })
).done(function(statsResp, estimatorResp) {
const stats = statsResp[0].stats;
const tokenCountEstimatorName = estimatorResp[0].token_count_estimator_name;
self.displayStats(stats, tokenCountEstimatorName);
}).fail(function() {
console.error('Error loading stats or estimator name');
});
}
clearStats() {
let self = this;
$.ajax({
@@ -227,8 +228,27 @@ class Dashboard {
});
}
displayStats(stats) {
displayStats(stats, tokenCountEstimatorName) {
const names = Object.keys(stats);
// If no stats collected
if (names.length === 0) {
// hide summary, charts, estimator name
$('#stats-summary').hide();
$('#estimator-name').hide();
$('.charts-container').hide();
// show no-stats message
$('#no-stats-message').show();
return;
} else {
// Ensure everything is visible
$('#estimator-name').show();
$('#stats-summary').show();
$('.charts-container').show();
$('#no-stats-message').hide();
}
$('#estimator-name').html(`<strong>Token count estimator:</strong> ${tokenCountEstimatorName}`);
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);
@@ -252,18 +272,6 @@ class Dashboard {
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);
@@ -186,6 +186,11 @@
</div>
<div id="stats-summary" style="margin-bottom:20px; text-align:center;"></div>
<div id="estimator-name" style="text-align:center; margin-bottom:10px;"></div>
<div id="no-stats-message" style="text-align:center; color:#666; font-style:italic; display:none;">
No tool stats collected. Have you enabled tool stats collection in the configuration?
</div>
<div class="charts-container">
<div class="chart-group">