-
-
Notifications
You must be signed in to change notification settings - Fork 181
feat(leaderboard): provider avg-cost metrics and cache-hit model drilldown #753
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,7 @@ import type { | |
| DateRangeParams, | ||
| LeaderboardEntry, | ||
| LeaderboardPeriod, | ||
| ModelCacheHitStat, | ||
| ModelLeaderboardEntry, | ||
| ProviderCacheHitRateLeaderboardEntry, | ||
| ProviderLeaderboardEntry, | ||
|
|
@@ -28,7 +29,11 @@ interface LeaderboardViewProps { | |
|
|
||
| type LeaderboardScope = "user" | "provider" | "providerCacheHitRate" | "model"; | ||
| type UserEntry = LeaderboardEntry & { totalCostFormatted?: string }; | ||
| type ProviderEntry = ProviderLeaderboardEntry & { totalCostFormatted?: string }; | ||
| type ProviderEntry = ProviderLeaderboardEntry & { | ||
| totalCostFormatted?: string; | ||
| avgCostPerRequestFormatted?: string | null; | ||
| avgCostPerMillionTokensFormatted?: string | null; | ||
| }; | ||
| type ProviderCacheHitRateEntry = ProviderCacheHitRateLeaderboardEntry; | ||
| type ModelEntry = ModelLeaderboardEntry & { totalCostFormatted?: string }; | ||
| type AnyEntry = UserEntry | ProviderEntry | ProviderCacheHitRateEntry | ModelEntry; | ||
|
|
@@ -163,7 +168,7 @@ export function LeaderboardView({ isAdmin }: LeaderboardViewProps) { | |
| scope === "user" | ||
| ? 5 | ||
| : scope === "provider" | ||
| ? 8 | ||
| ? 10 | ||
| : scope === "providerCacheHitRate" | ||
| ? 8 | ||
| : scope === "model" | ||
|
|
@@ -265,6 +270,28 @@ export function LeaderboardView({ isAdmin }: LeaderboardViewProps) { | |
| sortKey: "avgTokensPerSecond", | ||
| getValue: (row) => (row as ProviderEntry).avgTokensPerSecond ?? 0, | ||
| }, | ||
| { | ||
| header: t("columns.avgCostPerRequest"), | ||
| className: "text-right font-mono", | ||
| cell: (row) => { | ||
| const r = row as ProviderEntry; | ||
| if (r.avgCostPerRequest == null) return "-"; | ||
| return r.avgCostPerRequestFormatted ?? r.avgCostPerRequest.toFixed(4); | ||
| }, | ||
| sortKey: "avgCostPerRequest", | ||
| getValue: (row) => (row as ProviderEntry).avgCostPerRequest ?? 0, | ||
| }, | ||
| { | ||
| header: t("columns.avgCostPerMillionTokens"), | ||
| className: "text-right font-mono", | ||
| cell: (row) => { | ||
| const r = row as ProviderEntry; | ||
| if (r.avgCostPerMillionTokens == null) return "-"; | ||
| return r.avgCostPerMillionTokensFormatted ?? r.avgCostPerMillionTokens.toFixed(2); | ||
| }, | ||
| sortKey: "avgCostPerMillionTokens", | ||
| getValue: (row) => (row as ProviderEntry).avgCostPerMillionTokens ?? 0, | ||
| }, | ||
| ]; | ||
|
|
||
| const providerCacheHitRateColumns: ColumnDef<ProviderCacheHitRateEntry>[] = [ | ||
|
|
@@ -484,7 +511,70 @@ export function LeaderboardView({ isAdmin }: LeaderboardViewProps) { | |
| </CardContent> | ||
| </Card> | ||
| ) : ( | ||
| <LeaderboardTable data={data} period={period} columns={columns} getRowKey={rowKey} /> | ||
| <LeaderboardTable | ||
| data={data} | ||
| period={period} | ||
| columns={columns} | ||
| getRowKey={rowKey} | ||
| renderExpandedContent={ | ||
| scope === "providerCacheHitRate" | ||
| ? (row) => { | ||
| const entry = row as ProviderCacheHitRateEntry & { | ||
| modelStats?: ModelCacheHitStat[]; | ||
| }; | ||
| if (!entry.modelStats || entry.modelStats.length === 0) return null; | ||
| return ( | ||
| <div className="px-8 py-3"> | ||
| <div className="text-xs text-muted-foreground mb-2 font-medium"> | ||
|
Comment on lines
+526
to
+528
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Expand label is inverted The expanded section currently renders Prompt To Fix With AIThis is a comment left during a code review.
Path: src/app/[locale]/dashboard/leaderboard/_components/leaderboard-view.tsx
Line: 526:528
Comment:
**Expand label is inverted**
The expanded section currently renders `{t("expandModelStats")}` even when the row is already expanded. This makes the UI text incorrect in the expanded state and the new `collapseModelStats` i18n key is never used. Use `collapseModelStats` when the row is expanded (and `expandModelStats` when collapsed), or remove the unused key.
How can I resolve this? If you propose a fix, please make it concise. |
||
| {t("expandModelStats")} | ||
| </div> | ||
|
Comment on lines
+527
to
+530
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
此处已经是展开后的内容区域,但使用了 🤖 Prompt for AI Agents |
||
| <table className="w-full text-sm"> | ||
| <thead> | ||
| <tr className="text-xs text-muted-foreground border-b"> | ||
| <th className="text-left py-1 pr-4">{t("columns.model")}</th> | ||
| <th className="text-right py-1 pr-4">{t("columns.requests")}</th> | ||
| <th className="text-right py-1 pr-4"> | ||
| {t("columns.cacheReadTokens")} | ||
| </th> | ||
| <th className="text-right py-1 pr-4">{t("columns.totalTokens")}</th> | ||
| <th className="text-right py-1">{t("columns.cacheHitRate")}</th> | ||
| </tr> | ||
| </thead> | ||
| <tbody> | ||
| {entry.modelStats.map((ms) => { | ||
| const rate = (ms.cacheHitRate ?? 0) * 100; | ||
| const colorClass = | ||
| rate >= 85 | ||
| ? "text-green-600 dark:text-green-400" | ||
| : rate >= 60 | ||
| ? "text-yellow-600 dark:text-yellow-400" | ||
| : "text-orange-600 dark:text-orange-400"; | ||
| return ( | ||
| <tr key={ms.model} className="border-b last:border-b-0"> | ||
| <td className="py-1 pr-4 font-mono">{ms.model}</td> | ||
| <td className="text-right py-1 pr-4"> | ||
| {ms.totalRequests.toLocaleString()} | ||
| </td> | ||
| <td className="text-right py-1 pr-4"> | ||
| {formatTokenAmount(ms.cacheReadTokens)} | ||
| </td> | ||
| <td className="text-right py-1 pr-4"> | ||
| {formatTokenAmount(ms.totalInputTokens)} | ||
| </td> | ||
| <td className={`text-right py-1 ${colorClass}`}> | ||
| {rate.toFixed(1)}% | ||
| </td> | ||
| </tr> | ||
| ); | ||
| })} | ||
| </tbody> | ||
| </table> | ||
| </div> | ||
| ); | ||
| } | ||
| : undefined | ||
| } | ||
| /> | ||
| )} | ||
| </div> | ||
| </div> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[HIGH] [LOGIC-BUG] Sorting
nullavg-cost values as0makes "no data" providers look cheapestEvidence (new code)
src/app/[locale]/dashboard/leaderboard/_components/leaderboard-view.tsx:282src/app/[locale]/dashboard/leaderboard/_components/leaderboard-view.tsx:293Backend defines these as
nullwhen the denominator is 0 (e.g.avgCostPerRequest: number | null; // ... null when totalRequests === 0insrc/repository/leaderboard.ts). The UI also renders"-"when the value isnull, so sorting them as0is misleading (first click is ascending).Suggested fix: allow
getValueto returnnulland makeLeaderboardTablesort nulls last for both directions.