mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 08:41:23 +00:00
Web UI: add token usage dashboard (#10072)
* feat(ui): Token Usage dashboard with session analytics Adds a comprehensive Token Usage view to the dashboard: Backend: - Extended session-cost-usage.ts with per-session daily breakdown - Added date range filtering (startMs/endMs) to API endpoints - New sessions.usage, sessions.usage.timeseries, sessions.usage.logs endpoints - Cost breakdown by token type (input/output/cache read/write) Frontend: - Two-column layout: Daily chart + breakdown | Sessions list - Interactive daily bar chart with click-to-filter and shift-click range select - Session detail panel with usage timeline, conversation logs, context weight - Filter chips for active day/session selections - Toggle between tokens/cost view modes (default: cost) - Responsive design for smaller screens UX improvements: - 21-day default date range - Debounced date input (400ms) - Session list shows filtered totals when days selected - Context weight breakdown shows skills, tools, files contribution * fix(ui): restore gatewayUrl validation and syncUrlWithSessionKey signature - Restore normalizeGatewayUrl() to validate ws:/wss: protocol - Restore isTopLevelWindow() guard for iframe security - Revert syncUrlWithSessionKey signature (host param was unused) * feat(ui): Token Usage dashboard with session analytics Adds a comprehensive Token Usage view to the dashboard: Backend: - Extended session-cost-usage.ts with per-session daily breakdown - Added date range filtering (startMs/endMs) to API endpoints - New sessions.usage, sessions.usage.timeseries, sessions.usage.logs endpoints - Cost breakdown by token type (input/output/cache read/write) Frontend: - Two-column layout: Daily chart + breakdown | Sessions list - Interactive daily bar chart with click-to-filter and shift-click range select - Session detail panel with usage timeline, conversation logs, context weight - Filter chips for active day/session selections - Toggle between tokens/cost view modes (default: cost) - Responsive design for smaller screens UX improvements: - 21-day default date range - Debounced date input (400ms) - Session list shows filtered totals when days selected - Context weight breakdown shows skills, tools, files contribution * fix: usage dashboard data + cost handling (#8462) (thanks @mcinteerj) * Usage: enrich metrics dashboard * Usage: add latency + model trends * Gateway: improve usage log parsing * UI: add usage query helpers * UI: client-side usage filter + debounce * Build: harden write-cli-compat timing * UI: add conversation log filters * UI: fix usage dashboard lint + state * Web UI: default usage dates to local day * Protocol: sync session usage params (#8462) (thanks @mcinteerj, @TakHoffman) --------- Co-authored-by: Jake McInteer <mcinteerj@gmail.com>
This commit is contained in:
@@ -3,6 +3,7 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
import type { SessionPreviewItem } from "./session-utils.types.js";
|
||||
import { resolveSessionTranscriptPath } from "../config/sessions.js";
|
||||
import { extractToolCallNames, hasToolCall } from "../utils/transcript-tools.js";
|
||||
import { stripEnvelope } from "./chat-sanitize.js";
|
||||
|
||||
export function readSessionMessages(
|
||||
@@ -292,35 +293,11 @@ function extractPreviewText(message: TranscriptPreviewMessage): string | null {
|
||||
}
|
||||
|
||||
function isToolCall(message: TranscriptPreviewMessage): boolean {
|
||||
if (message.toolName || message.tool_name) {
|
||||
return true;
|
||||
}
|
||||
if (!Array.isArray(message.content)) {
|
||||
return false;
|
||||
}
|
||||
return message.content.some((entry) => {
|
||||
if (entry?.name) {
|
||||
return true;
|
||||
}
|
||||
const raw = typeof entry?.type === "string" ? entry.type.toLowerCase() : "";
|
||||
return raw === "toolcall" || raw === "tool_call";
|
||||
});
|
||||
return hasToolCall(message as Record<string, unknown>);
|
||||
}
|
||||
|
||||
function extractToolNames(message: TranscriptPreviewMessage): string[] {
|
||||
const names: string[] = [];
|
||||
if (Array.isArray(message.content)) {
|
||||
for (const entry of message.content) {
|
||||
if (typeof entry?.name === "string" && entry.name.trim()) {
|
||||
names.push(entry.name.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
const toolName = typeof message.toolName === "string" ? message.toolName : message.tool_name;
|
||||
if (typeof toolName === "string" && toolName.trim()) {
|
||||
names.push(toolName.trim());
|
||||
}
|
||||
return names;
|
||||
return extractToolCallNames(message as Record<string, unknown>);
|
||||
}
|
||||
|
||||
function extractMediaSummary(message: TranscriptPreviewMessage): string | null {
|
||||
|
||||
Reference in New Issue
Block a user