feat: add Reset Stats button to admin LLM tracking panel

Allows resetting the LLM stats aggregation window without deleting
historical data. Stores a reset timestamp in metadata and filters
the aggregation query to only include calls after that point.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
root
2026-03-13 01:59:24 +00:00
parent 513d35e0e5
commit b2ac0e7a00
8 changed files with 78 additions and 1 deletions

View File

@@ -164,6 +164,10 @@ export class SocketClient {
this.socket.emit('admin-toggle-llm-tracking', { enabled });
}
resetLlmStats(): void {
this.socket.emit('admin-reset-llm-stats');
}
subscribeLogs(): void {
this.socket.emit('log-subscribe');
}

View File

@@ -336,6 +336,9 @@ export class GameScene extends Phaser.Scene {
this.adminPanel.onToggleLlmTracking = (enabled) => {
this.client.toggleLlmTracking(enabled);
};
this.adminPanel.onResetLlmStats = () => {
this.client.resetLlmStats();
};
this.client.onLlmStatsResult = (data) => {
this.adminPanel.updateLlmStats(data);

View File

@@ -46,6 +46,7 @@ export class AdminPanel {
onReset: (() => void) | null = null;
onRequestLlmStats: (() => void) | null = null;
onToggleLlmTracking: ((enabled: boolean) => void) | null = null;
onResetLlmStats: (() => void) | null = null;
private statsColumn: HTMLDivElement | null = null;
private trackingToggle: HTMLInputElement | null = null;
@@ -263,6 +264,26 @@ export class AdminPanel {
totalsRow.appendChild(totalsCost);
this.statsColumn.appendChild(totalsRow);
// Reset Stats button
const resetStatsBtn = document.createElement('button');
resetStatsBtn.style.cssText = `
background: #2a2a4e;
border: 1px solid #8878a8;
color: #e0d0b0;
font-family: 'Press Start 2P', monospace;
font-size: 8px;
padding: 5px 10px;
cursor: pointer;
margin-top: 8px;
flex-shrink: 0;
align-self: center;
`;
resetStatsBtn.textContent = 'Reset Stats';
resetStatsBtn.addEventListener('click', () => {
this.onResetLlmStats?.();
});
this.statsColumn.appendChild(resetStatsBtn);
}
private buildEditor(constants: TunableConstants): void {

View File

@@ -0,0 +1,22 @@
# LLM Stats Reset Button
## Goal
Add a "Reset Stats" button to the Admin panel's LLM tracking column that gives a clean aggregation window without deleting historical data.
## Behavior
1. "Reset Stats" button appears at the bottom of the right column, below the TOTAL row.
2. Clicking it emits `admin-reset-llm-stats` socket event.
3. Server stores the current timestamp in the `metadata` table as `llm_stats_reset_after`.
4. Server's `getAggregatedStats()` adds `WHERE timestamp > ?` using that value.
5. Server responds with updated (now-empty) stats via existing `admin-llm-stats-result`.
6. Client renders the refreshed stats as usual.
## Changes
- **`server/src/llm/llmStatsService.ts`** — `getAggregatedStats()` reads reset timestamp from metadata, filters query. New `resetStats()` method to store the timestamp.
- **`server/src/network/SocketServer.ts`** — Handle `admin-reset-llm-stats` event: call `resetStats()`, respond with fresh stats.
- **`client/src/ui/AdminPanel.ts`** — Add "Reset Stats" button in `updateLlmStats()`, wire click to new callback.
- **`client/src/scenes/GameScene.ts`** — Wire the new callback through to SocketClient.
- **`client/src/network/SocketClient.ts`** — Add `resetLlmStats()` method emitting the event.

View File

@@ -16,6 +16,7 @@ export interface LlmStatsService {
getStats(): LlmStatsData;
isTrackingEnabled(): boolean;
setTrackingEnabled(enabled: boolean): void;
resetStats(): void;
}
export function createLlmStatsService(): LlmStatsService {
@@ -52,6 +53,12 @@ export function createLlmStatsService(): LlmStatsService {
const enabled = loadTrackingEnabled();
try {
const db = getDatabase();
const resetRow = db.prepare("SELECT value FROM metadata WHERE key = 'llm_stats_reset_after'").get() as { value: string } | undefined;
const resetAfter = resetRow?.value ?? null;
const whereClause = resetAfter ? 'WHERE timestamp > ?' : '';
const params = resetAfter ? [resetAfter] : [];
const rows = db.prepare(`
SELECT
template_name,
@@ -63,9 +70,10 @@ export function createLlmStatsService(): LlmStatsService {
SUM(CASE WHEN retries > 0 THEN 1 ELSE 0 END) as retry_count,
SUM(failed) as fail_count
FROM llm_call_log
${whereClause}
GROUP BY template_name
ORDER BY count DESC
`).all() as Array<{
`).all(...params) as Array<{
template_name: string;
count: number;
total_cost: number;
@@ -114,5 +122,14 @@ export function createLlmStatsService(): LlmStatsService {
// ignore
}
},
resetStats(): void {
try {
const db = getDatabase();
db.prepare("INSERT OR REPLACE INTO metadata (key, value) VALUES ('llm_stats_reset_after', ?)").run(new Date().toISOString());
} catch {
// ignore
}
},
};
}

View File

@@ -204,6 +204,14 @@ export class SocketServer {
socket.emit('admin-llm-stats-result', stats);
});
socket.on('admin-reset-llm-stats', () => {
if (!this.authenticatedSockets.has(socket.id)) return;
this.gameLoop.llmStatsService.resetStats();
const stats = this.gameLoop.llmStatsService.getStats();
socket.emit('admin-llm-stats-result', stats);
this.gameLoop.logService.log('info', 'Game', 'Admin reset LLM stats');
});
socket.on('admin-toggle-llm-tracking', (data: { enabled: boolean }) => {
if (!this.authenticatedSockets.has(socket.id)) return;
this.gameLoop.llmStatsService.setTrackingEnabled(data.enabled);

View File

@@ -393,4 +393,5 @@ export interface ClientEvents {
'admin-toggle-llm-tracking': (data: {
enabled: boolean;
}) => void;
'admin-reset-llm-stats': () => void;
}

View File

@@ -382,4 +382,5 @@ export interface ClientEvents {
'log-unsubscribe': () => void;
'admin-llm-stats': () => void;
'admin-toggle-llm-tracking': (data: { enabled: boolean }) => void;
'admin-reset-llm-stats': () => void;
}