feat: show cache rate in billing request list and details

This commit is contained in:
2026-09-10 15:46:21 +08:00 Unverified
parent 78c64b4b3d
commit 19c2143ea8
4 changed files with 54 additions and 43 deletions
+2
View File
@@ -44,6 +44,8 @@ The existing `window.BILLING_UI_CONFIG` contract and management-key discovery re
Requests retain server pagination and filters. Notes/history use their operation category as the display type and action as the endpoint label. Complete paths, timing and all usage fields remain available in the detail sheet. Auxiliary operations have no synthetic token charge.
The request table and detail sheet display cache rate as cache-read tokens divided by input tokens, using the API's percentage value with one decimal place. Auxiliary operations, zero-input requests, and records without a valid cache rate display an em dash; measured zero cache hits display `0.0%`.
## Layout
Keep the 46px header on one row, without a subtitle. Pages start with their data or a single search/action toolbar, without introductory headings or descriptions. The overview preserves the user totals table, seven-day stacked cost chart, and quota/concurrency bars in that order. Requests use compact single-line rows. User and model cards open on click or keyboard activation; user status and model pricing conditions appear beside their names. Keep the long-context price row present even when no rates are configured.
+4
View File
@@ -22,6 +22,10 @@ export const duration = (value: unknown) =>
: Number(value) >= 1000
? (Number(value) / 1000).toFixed(2) + " s"
: Number(value) + " ms";
export const cacheRate = (row: Row) =>
operation(row).auxiliary || !(row.input_tokens > 0) || !Number.isFinite(row.cache_rate)
? "—"
: row.cache_rate.toFixed(1) + "%";
export function operation(row: Row) {
const match = String(row.endpoint || "").match(/\/alpha\/(notes|history)\/v2\/([^/?]+)/);
if (match) return { type: match[1], name: match[2], auxiliary: true };
+6 -1
View File
@@ -31,7 +31,7 @@ import {
CopyButton,
} from "@/components/common";
import { useData, useAPI, type Row } from "@/lib/api";
import { operation, dateTime, compact, number, money, duration } from "@/lib/format";
import { operation, dateTime, compact, number, money, duration, cacheRate } from "@/lib/format";
export function Requests() {
const api = useAPI(),
@@ -188,6 +188,9 @@ export function Requests() {
<TableHead className="text-right">首字延迟</TableHead>
<TableHead className="text-right">生成速度</TableHead>
<TableHead className="text-right">Token</TableHead>
<TableHead className="text-right" title="缓存读取 Token ÷ 输入 Token × 100%">
缓存率
</TableHead>
<TableHead className="text-right">费用</TableHead>
</TableRow>
</TableHeader>
@@ -253,6 +256,7 @@ export function Requests() {
<TableCell className="text-right tabular-nums">
{op.auxiliary ? "—" : compact(r.total_tokens)}
</TableCell>
<TableCell className="text-right tabular-nums">{cacheRate(r)}</TableCell>
<TableCell className="text-right tabular-nums">
{r.cost_available ? money(r.cost_usd, 4) : "—"}
</TableCell>
@@ -303,6 +307,7 @@ export function Requests() {
["输出 Token", number(selected.output_tokens)],
["推理 Token", number(selected.reasoning_tokens)],
["缓存读取", number(selected.cache_read_tokens)],
["缓存率", cacheRate(selected)],
["缓存写入", number(selected.cache_write_tokens)],
["总 Token", number(selected.total_tokens)],
["首字延迟", operation(selected).auxiliary ? "—" : duration(selected.ttft_ms)],
+42 -42
View File
File diff suppressed because one or more lines are too long