diff --git a/messages/en/dashboard.json b/messages/en/dashboard.json index 5ed3ad430..752ed7c37 100644 --- a/messages/en/dashboard.json +++ b/messages/en/dashboard.json @@ -704,6 +704,7 @@ "cacheHitRequests": "Cache-eligible Requests", "cacheHitRate": "Cache Hit Rate", "cacheCoefficient": "Cache Coefficient", + "cacheCoefficientTooltip": "A higher cache coefficient means fewer provider account switches and less noticeable cache degradation. 0.9+ is excellent, 0.8+ is good.", "cacheReadTokens": "Cache Read Tokens", "totalTokens": "Total Tokens", "cacheCreationConsumedAmount": "Cache Creation Spend", diff --git a/messages/ja/dashboard.json b/messages/ja/dashboard.json index 5bfb26738..0f49373bb 100644 --- a/messages/ja/dashboard.json +++ b/messages/ja/dashboard.json @@ -704,6 +704,7 @@ "cacheHitRequests": "キャッシュ対象リクエスト数(命中率計算対象)", "cacheHitRate": "キャッシュ命中率", "cacheCoefficient": "キャッシュ係数", + "cacheCoefficientTooltip": "キャッシュ係数が大きいほど、プロバイダーのアカウント切り替えが少なく、キャッシュ劣化が目立ちにくくなります。0.9 以上は優秀、0.8 以上は良好です。", "cacheReadTokens": "キャッシュ読取トークン数", "totalTokens": "総トークン数", "cacheCreationConsumedAmount": "キャッシュ作成消費額", diff --git a/messages/ru/dashboard.json b/messages/ru/dashboard.json index 453a0e427..5b630c199 100644 --- a/messages/ru/dashboard.json +++ b/messages/ru/dashboard.json @@ -704,6 +704,7 @@ "cacheHitRequests": "Запросы (учтены в hit rate)", "cacheHitRate": "Попадания в кэш", "cacheCoefficient": "Коэффициент кэша", + "cacheCoefficientTooltip": "Чем выше коэффициент кэша, тем реже переключаются аккаунты поставщика и тем менее заметна деградация кэша. 0.9 и выше — отлично, 0.8 и выше — хорошо.", "cacheReadTokens": "Токены чтения из кэша", "totalTokens": "Всего токенов", "cacheCreationConsumedAmount": "Расход на создание кэша", diff --git a/messages/zh-CN/dashboard.json b/messages/zh-CN/dashboard.json index 9b1d50786..5b07a9346 100644 --- a/messages/zh-CN/dashboard.json +++ b/messages/zh-CN/dashboard.json @@ -704,6 +704,7 @@ "cacheHitRequests": "缓存触发请求数", "cacheHitRate": "缓存命中率", "cacheCoefficient": "缓存系数", + "cacheCoefficientTooltip": "缓存系数越大,供应商切号越少,失缓现象越不明显。0.9 以上为优秀,0.8 以上为良好。", "cacheReadTokens": "缓存读取 Token 数", "totalTokens": "总 Token 数", "cacheCreationConsumedAmount": "缓存创建消耗金额", diff --git a/messages/zh-TW/dashboard.json b/messages/zh-TW/dashboard.json index 112d7afde..b69873d4b 100644 --- a/messages/zh-TW/dashboard.json +++ b/messages/zh-TW/dashboard.json @@ -704,6 +704,7 @@ "cacheHitRequests": "快取命中請求數(納入快取命中率計算的請求總數)", "cacheHitRate": "快取命中率", "cacheCoefficient": "快取係數", + "cacheCoefficientTooltip": "快取係數越大,供應商切號越少,失緩現象越不明顯。0.9 以上為優秀,0.8 以上為良好。", "cacheReadTokens": "快取讀取 Token 數", "totalTokens": "總 Token 數", "cacheCreationConsumedAmount": "快取建立消耗金額", diff --git a/src/app/[locale]/dashboard/leaderboard/_components/leaderboard-table.tsx b/src/app/[locale]/dashboard/leaderboard/_components/leaderboard-table.tsx index 2f8438eb9..ea995d44d 100644 --- a/src/app/[locale]/dashboard/leaderboard/_components/leaderboard-table.tsx +++ b/src/app/[locale]/dashboard/leaderboard/_components/leaderboard-table.tsx @@ -7,6 +7,7 @@ import { Award, ChevronDown, ChevronRight, + CircleHelp, Medal, Trophy, } from "lucide-react"; @@ -22,11 +23,14 @@ import { TableHeader, TableRow, } from "@/components/ui/table"; +import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import type { LeaderboardPeriod } from "@/repository/leaderboard"; // 支持动态列定义 export interface ColumnDef { header: string; + /** 表头帮助图标悬停时展示的说明文案 */ + headerTooltip?: string; className?: string; /** * index 语义: @@ -245,6 +249,23 @@ export function LeaderboardTable({ className={`flex items-center ${col.className?.includes("text-right") ? "justify-end" : ""} ${shouldBold ? "font-bold" : ""}`} > {col.header} + {col.headerTooltip && ( + + + + + + {col.headerTooltip} + + + )} {col.sortKey && getSortIcon(col.sortKey)} diff --git a/src/app/[locale]/dashboard/leaderboard/_components/leaderboard-view.tsx b/src/app/[locale]/dashboard/leaderboard/_components/leaderboard-view.tsx index 060d519f5..87aa57966 100644 --- a/src/app/[locale]/dashboard/leaderboard/_components/leaderboard-view.tsx +++ b/src/app/[locale]/dashboard/leaderboard/_components/leaderboard-view.tsx @@ -101,6 +101,21 @@ function renderSuccessRateCell( const VALID_PERIODS: LeaderboardPeriod[] = ["daily", "weekly", "monthly", "allTime", "custom"]; +// 缓存系数分层配色(与缓存命中率同档):>=0.9 优秀(绿),>=0.8 良好(黄),其余橙色 +function renderCacheCoefficientCell(bp: number | null) { + if (bp == null) { + return ; + } + const value = bp / 10000; + const colorClass = + value >= 0.9 + ? "text-green-600 dark:text-green-400" + : value >= 0.8 + ? "text-yellow-600 dark:text-yellow-400" + : "text-orange-600 dark:text-orange-400"; + return {value.toFixed(2)}; +} + export function LeaderboardView({ isAdmin }: LeaderboardViewProps) { const t = useTranslations("dashboard.leaderboard"); const searchParams = useSearchParams(); @@ -385,11 +400,10 @@ export function LeaderboardView({ isAdmin }: LeaderboardViewProps) { }, { header: t("columns.cacheCoefficient"), + headerTooltip: t("columns.cacheCoefficientTooltip"), className: "text-right", - cell: (row) => { - const bp = "cacheCoefficientBp" in row ? row.cacheCoefficientBp : null; - return bp == null ? "–" : (bp / 10000).toFixed(2); - }, + cell: (row) => + renderCacheCoefficientCell("cacheCoefficientBp" in row ? row.cacheCoefficientBp : null), sortKey: "cacheCoefficientBp", getValue: (row) => ("cacheCoefficientBp" in row ? row.cacheCoefficientBp : null), }, @@ -430,11 +444,10 @@ export function LeaderboardView({ isAdmin }: LeaderboardViewProps) { }, { header: t("columns.cacheCoefficient"), + headerTooltip: t("columns.cacheCoefficientTooltip"), className: "text-right", - cell: (row) => { - const bp = "cacheCoefficientBp" in row ? row.cacheCoefficientBp : null; - return bp == null ? "–" : (bp / 10000).toFixed(2); - }, + cell: (row) => + renderCacheCoefficientCell("cacheCoefficientBp" in row ? row.cacheCoefficientBp : null), sortKey: "cacheCoefficientBp", getValue: (row) => ("cacheCoefficientBp" in row ? row.cacheCoefficientBp : null), }, diff --git a/src/app/[locale]/dashboard/logs/_components/error-details-dialog/components/SummaryTab.tsx b/src/app/[locale]/dashboard/logs/_components/error-details-dialog/components/SummaryTab.tsx index 522a7a293..b20ea4a2f 100644 --- a/src/app/[locale]/dashboard/logs/_components/error-details-dialog/components/SummaryTab.tsx +++ b/src/app/[locale]/dashboard/logs/_components/error-details-dialog/components/SummaryTab.tsx @@ -349,7 +349,7 @@ export function SummaryTab({ {identity.value} diff --git a/src/app/[locale]/dashboard/logs/_components/filters/quick-filters-bar.tsx b/src/app/[locale]/dashboard/logs/_components/filters/quick-filters-bar.tsx index c6e0f7b3c..dcbdfd70f 100644 --- a/src/app/[locale]/dashboard/logs/_components/filters/quick-filters-bar.tsx +++ b/src/app/[locale]/dashboard/logs/_components/filters/quick-filters-bar.tsx @@ -8,12 +8,16 @@ import { cn } from "@/lib/utils"; export type FilterPreset = "today" | "this-week" | "errors-only" | "show-retries"; interface QuickFiltersBarProps { - activePreset: FilterPreset | null; + activePresets: ReadonlySet; onPresetToggle: (preset: FilterPreset) => void; className?: string; } -export function QuickFiltersBar({ activePreset, onPresetToggle, className }: QuickFiltersBarProps) { +export function QuickFiltersBar({ + activePresets, + onPresetToggle, + className, +}: QuickFiltersBarProps) { const t = useTranslations("dashboard.logs.filters"); const timePresets: Array<{ id: FilterPreset; label: string; icon: typeof Calendar }> = [ @@ -40,10 +44,11 @@ export function QuickFiltersBar({ activePreset, onPresetToggle, className }: Qui