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

@@ -10,11 +10,11 @@ import { useTimeRange } from "@/lib/time-range-context";
import { useI18n } from "@/lib/i18n";
type Tab = "user" | "model" | "channel";
type SortKey = "calls" | "prompt_tokens" | "completion_tokens" | "total_tokens";
type SortKey = "calls" | "prompt_tokens" | "completion_tokens" | "cache_creation_tokens" | "cache_read_tokens" | "total_tokens";
interface RankItem {
rank: number; name: string; username?: string; id?: number; 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;
}
export default function RankingsPage() {
@@ -70,6 +70,8 @@ export default function RankingsPage() {
{ key: null, label: t("th.name"), align: "left" },
{ key: "calls", label: t("th.calls"), align: "right" },
{ key: "prompt_tokens", label: t("th.input"), align: "right" },
{ key: "cache_creation_tokens", label: t("th.cacheCreation"), align: "right" },
{ key: "cache_read_tokens", label: t("th.cacheRead"), align: "right" },
{ key: "completion_tokens", label: t("th.output"), align: "right" },
{ key: "total_tokens", label: t("th.totalToken"), align: "right" },
];
@@ -125,7 +127,7 @@ export default function RankingsPage() {
</thead>
<tbody>
{loading ? (
<tr><td colSpan={6} 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={8} 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) => (
<motion.tr key={item.name} initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ delay: i * 0.02 }}
className="row-glow transition-colors" style={{ borderBottom: "1px solid var(--surface-border)" }}>
@@ -135,6 +137,8 @@ export default function RankingsPage() {
</td>
<td className="px-4 py-3 text-right tabular-nums" 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>
</motion.tr>