feat: add usage stats key renderer

This commit is contained in:
c4ch3c4d3
2026-08-14 12:55:22 -06:00
parent f1a5f89e0f
commit 4c4ec0e8fd
2 changed files with 102 additions and 1 deletions
+67
View File
@@ -1,5 +1,6 @@
import { type GpuMetricKind } from "./metrics-parser"; import { type GpuMetricKind } from "./metrics-parser";
import { type ModelRuntimeState } from "./inflight-tracker"; import { type ModelRuntimeState } from "./inflight-tracker";
import { type UsageStats } from "./stats";
import { escapeXml, shorten } from "./util"; import { escapeXml, shorten } from "./util";
export function svgDataUrl(svg: string): string { export function svgDataUrl(svg: string): string {
@@ -188,3 +189,69 @@ function frame(bg: string, parts: string[]): string {
${parts.join("\n ")} ${parts.join("\n ")}
</svg>`; </svg>`;
} }
export function formatCompact(n: number): string {
if (!Number.isFinite(n)) return "--";
if (n < 1000) return `${Math.round(n)}`;
if (n < 1_000_000) return `${(n / 1000).toFixed(1)}k`;
return `${(n / 1_000_000).toFixed(1)}M`;
}
export interface UsageRenderOptions {
modelName: string;
stats?: UsageStats;
primaryStat: "requests" | "input_tokens" | "output_tokens" | "gen_p95";
offline: boolean;
}
export function renderUsage(opts: UsageRenderOptions): string {
const name = escapeXml(shorten(opts.modelName === "all" ? "ALL MODELS" : opts.modelName));
if (opts.offline) {
return frame("#10131a", [
centerText("!!", 34, 20, "bold", "#e0e0e0"),
centerText("OFFLINE", 52, 9, "normal", "#bdbdbd"),
centerText(name, 64, 7, "normal", "#ffffff"),
]);
}
const s = opts.stats;
const big = s ? formatCompact(primaryValue(s, opts.primaryStat)) : "--";
const rows = usageRows(s, opts.primaryStat);
const parts: string[] = [
centerText(name, 10, 7, "normal", "#ffffff"),
centerText(big, 36, 24, "bold", "#ffffff"),
];
for (const row of rows) {
parts.push(
`<text x="4" y="${row.y}" font-family="Arial,sans-serif" font-size="7" fill="#8b93a5">${row.label}</text>`,
`<text x="68" y="${row.y}" text-anchor="end" font-family="Arial,sans-serif" font-size="7" fill="#ffffff">${row.value}</text>`,
);
}
return frame("#10131a", parts);
}
function primaryValue(s: UsageStats, primary: "requests" | "input_tokens" | "output_tokens" | "gen_p95"): number {
switch (primary) {
case "requests":
return s.totalRequests;
case "input_tokens":
return s.totalInputTokens;
case "output_tokens":
return s.totalOutputTokens;
case "gen_p95":
return s.genP95;
}
}
function usageRows(
s: UsageStats | undefined,
primary: "requests" | "input_tokens" | "output_tokens" | "gen_p95",
): { label: string; value: string; y: number }[] {
const items: { label: string; value: string }[] = [];
if (primary !== "requests") items.push({ label: "REQ", value: s ? formatCompact(s.totalRequests) : "--" });
if (primary !== "input_tokens") items.push({ label: "IN", value: s ? formatCompact(s.totalInputTokens) : "--" });
if (primary !== "output_tokens") items.push({ label: "OUT", value: s ? formatCompact(s.totalOutputTokens) : "--" });
if (primary !== "gen_p95") items.push({ label: "P95", value: s ? `${Math.round(s.genP95)} t/s` : "--" });
return items.map((it, i) => ({ ...it, y: 52 + i * 7 }));
}
+35 -1
View File
@@ -1,6 +1,6 @@
import assert from "node:assert/strict"; import assert from "node:assert/strict";
import { test } from "node:test"; import { test } from "node:test";
import { renderGpuGraph, renderInflight, svgDataUrl } from "../src/lib/render"; import { renderGpuGraph, renderInflight, renderUsage, svgDataUrl, formatCompact } from "../src/lib/render";
test("svgDataUrl wraps an SVG as a base64 data URL", () => { test("svgDataUrl wraps an SVG as a base64 data URL", () => {
const url = svgDataUrl("<svg></svg>"); const url = svgDataUrl("<svg></svg>");
@@ -99,3 +99,37 @@ test("renderGpuGraph: single-point history avoids divide-by-zero", () => {
const svg = renderGpuGraph({ gpuName: "ALL GPUS", metric: "util_percent", value: 50, history: [50], offline: false }); const svg = renderGpuGraph({ gpuName: "ALL GPUS", metric: "util_percent", value: 50, history: [50], offline: false });
assert.match(svg, /<svg/); assert.match(svg, /<svg/);
}); });
test("formatCompact renders integers, k, and M", () => {
assert.equal(formatCompact(0), "0");
assert.equal(formatCompact(999), "999");
assert.equal(formatCompact(52100), "52.1k");
assert.equal(formatCompact(1200000), "1.2M");
});
test("renderUsage shows the primary stat big and the rest small", () => {
const svg = renderUsage({
modelName: "DeepSeek-V4-Flash-0731",
stats: { totalRequests: 1950, totalInputTokens: 312177540, totalOutputTokens: 889193, genP95: 378.86 },
primaryStat: "gen_p95",
offline: false,
});
assert.match(svg, />379</);
assert.match(svg, /REQ/);
assert.match(svg, /IN/);
assert.match(svg, /OUT/);
assert.match(svg, /312.2M/);
assert.match(svg, /#10131a/);
});
test("renderUsage renders -- when stats are unknown and OFFLINE when offline", () => {
const none = renderUsage({ modelName: "A", stats: undefined, primaryStat: "requests", offline: false });
assert.match(none, />--</);
const off = renderUsage({ modelName: "A", stats: undefined, primaryStat: "requests", offline: true });
assert.match(off, /OFFLINE/);
});
test("renderUsage labels the aggregate view ALL MODELS", () => {
const svg = renderUsage({ modelName: "all", stats: { totalRequests: 1, totalInputTokens: 1, totalOutputTokens: 1, genP95: 1 }, primaryStat: "requests", offline: false });
assert.match(svg, /ALL MODELS/);
});