feat: 添加缓存 Token 独立展示(cache_creation / cache_read)

从 logs 表 other JSON 字段提取 cache_creation_tokens 和 cache_tokens,
在排名、日志、聚合、详情页分别展示,total_tokens 包含缓存部分。
This commit is contained in:
2026-04-20 19:55:09 +08:00
parent 004cdd9fc9
commit c5c91cc157
6 changed files with 104 additions and 27 deletions

View File

@@ -11,10 +11,10 @@ import { useI18n } from "@/lib/i18n";
interface AggItem {
rank: number; name: string; calls: number;
prompt_tokens: number; completion_tokens: number; total_tokens: number;
prompt_tokens: number; completion_tokens: number; cache_creation_tokens: number; cache_read_tokens: number; total_tokens: number;
}
type SortKey = "total_tokens" | "calls" | "prompt_tokens" | "completion_tokens" | "ratio";
type SortKey = "total_tokens" | "calls" | "prompt_tokens" | "completion_tokens" | "cache_creation_tokens" | "cache_read_tokens" | "ratio";
function RatioTooltip({ text }: { text: string }) {
const [show, setShow] = useState(false);
@@ -122,6 +122,8 @@ export default function AggregationPage() {
const sortHeaders: { key: SortKey; label: string }[] = [
{ key: "calls", label: t("th.calls") },
{ key: "prompt_tokens", label: t("th.input") },
{ key: "cache_creation_tokens", label: t("th.cacheCreation") },
{ key: "cache_read_tokens", label: t("th.cacheRead") },
{ key: "completion_tokens", label: t("th.output") },
{ key: "total_tokens", label: t("th.totalToken") },
];
@@ -181,7 +183,7 @@ export default function AggregationPage() {
</thead>
<tbody>
{loading ? (
<tr><td colSpan={8} className="px-4 py-16 text-center"><div className="inline-block h-5 w-5 animate-spin rounded-full spinner" /></td></tr>
<tr><td colSpan={10} className="px-4 py-16 text-center"><div className="inline-block h-5 w-5 animate-spin rounded-full spinner" /></td></tr>
) : sorted.map((item, i) => {
const pct = totals.tokens > 0 ? (item.total_tokens / totals.tokens * 100) : 0;
const ratio = item.prompt_tokens > 0 ? (item.completion_tokens / item.prompt_tokens) : 0;
@@ -191,6 +193,8 @@ export default function AggregationPage() {
<td className="px-4 py-3 font-medium transition-colors" style={{ color: "var(--text-accent)" }}>{item.name}</td>
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)]" style={{ color: "var(--text-secondary)" }}>{formatNumber(item.calls)}</td>
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-muted)" }}>{formatTokens(item.prompt_tokens)}</td>
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-muted)" }}>{formatTokens(item.cache_creation_tokens)}</td>
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-muted)" }}>{formatTokens(item.cache_read_tokens)}</td>
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-muted)" }}>{formatTokens(item.completion_tokens)}</td>
<td className="px-4 py-3 text-right tabular-nums font-medium font-[family-name:var(--font-geist-mono)]" style={{ color: "var(--text-primary)" }}>{formatTokens(item.total_tokens)}</td>
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: ratio >= 1 ? "var(--accent)" : "var(--text-muted)" }}>{ratio.toFixed(2)}</td>