From a4a434cfaf8180aaffa969d397d8681383c6d645 Mon Sep 17 00:00:00 2001 From: laodouuu <1257029425@qq.com> Date: Wed, 29 Jul 2026 23:29:59 +0800 Subject: [PATCH] fix(web): honor currency setting in benchmark costs (#113) --- .../src/features/benchmark/benchmark-page.tsx | 57 ++++++++++++++----- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/packages/web/src/features/benchmark/benchmark-page.tsx b/packages/web/src/features/benchmark/benchmark-page.tsx index 0af4a0b..2f64ff3 100644 --- a/packages/web/src/features/benchmark/benchmark-page.tsx +++ b/packages/web/src/features/benchmark/benchmark-page.tsx @@ -25,6 +25,8 @@ import { apiErrorText } from "../../lib/api-error"; import { useDocumentTitle } from "../../lib/use-document-title"; import { formatDateTime, formatMoney, formatScore, humanizeDuration } from "../../lib/format"; import { agentDisplayName, useProject } from "../../state/project"; +import { useTheme } from "../../state/theme"; +import type { Currency } from "../../state/theme"; import { AgentAvatar } from "../../components/ui/agent-avatar"; import { Chevron } from "../../components/ui/chevron"; import { Segmented } from "../../components/ui/segmented"; @@ -152,11 +154,11 @@ function metricLabel(metric: BenchmarkMetric): string { } /** Display format for a metric value (shared by y-axis ticks and the tooltip): score / cost / duration each have their own formatting rule. */ -function formatMetric(metric: BenchmarkMetric, v: number): string { +function formatMetric(metric: BenchmarkMetric, v: number, currency: Currency): string { return metric === "score" ? formatScore(v) : metric === "cost" - ? formatMoney(v) + ? formatMoney(v, currency) : humanizeDuration(v); } @@ -176,10 +178,12 @@ function MetricTrendChart({ evaluations, series, metric, + currency, }: { evaluations: BenchmarkEvaluation[]; series: EvaluationSeries[]; metric: BenchmarkMetric; + currency: Currency; }) { const [hover, setHover] = useState(null); const [ref, width] = useChartWidth(); @@ -194,7 +198,7 @@ function MetricTrendChart({ {width > 0 && ( formatMetric(metric, v)} + fmtY={(v) => formatMetric(metric, v, currency)} dates={dates} hover={hover} onHover={setHover} @@ -205,7 +209,7 @@ function MetricTrendChart({ <>

{formatDateTime(e.time)}

- {v === null ? "—" : formatMetric(metric, v)} + {v === null ? "—" : formatMetric(metric, v, currency)} {e.version !== undefined && ( v{e.version} )} @@ -277,7 +281,13 @@ function MetricTrendChart({ * disambiguate. Mounted under a keyed container per Benchmark: switching Benchmarks resets back * to "score". */ -function TrendSection({ evaluations }: { evaluations: BenchmarkEvaluation[] }) { +function TrendSection({ + evaluations, + currency, +}: { + evaluations: BenchmarkEvaluation[]; + currency: Currency; +}) { const [metric, setMetric] = useState("score"); const series = modelSeries(evaluations); const ids = series.map((s) => s.modelId).filter((v): v is string => v !== undefined); @@ -324,7 +334,12 @@ function TrendSection({ evaluations }: { evaluations: BenchmarkEvaluation[] }) { ))} )} - + ); } @@ -335,9 +350,11 @@ const CELL = "px-3 py-2"; function EvaluationRow({ agentId, evaluation, + currency, }: { agentId: string; evaluation: BenchmarkEvaluation; + currency: Currency; }) { const [open, setOpen] = useState(false); return ( @@ -365,7 +382,7 @@ function EvaluationRow({ {formatScore(evaluation.score)} - {formatMoney(evaluation.cost)} + {formatMoney(evaluation.cost, currency)} {evaluation.durationMs !== undefined ? humanizeDuration(evaluation.durationMs) : "—"} @@ -406,7 +423,7 @@ function EvaluationRow({ {evaluation.cases.map((c) => ( - + ))} @@ -437,7 +454,15 @@ function SessionLink({ agentId, sessionId }: { agentId: string; sessionId?: stri * of each run (#index + score / cost / duration / Session link); with the old format lacking * runs, it's not expandable and the case-level single Session link is used as before. */ -function CaseRow({ agentId, caseScore: c }: { agentId: string; caseScore: BenchmarkCaseScore }) { +function CaseRow({ + agentId, + caseScore: c, + currency, +}: { + agentId: string; + caseScore: BenchmarkCaseScore; + currency: Currency; +}) { const [open, setOpen] = useState(false); const runs = c.runs ?? []; const expandable = runs.length > 0; @@ -455,7 +480,7 @@ function CaseRow({ agentId, caseScore: c }: { agentId: string; caseScore: Benchm {formatScore(c.score)} - {formatMoney(c.cost)} + {formatMoney(c.cost, currency)} {c.durationMs !== undefined ? humanizeDuration(c.durationMs) : "—"} @@ -472,7 +497,7 @@ function CaseRow({ agentId, caseScore: c }: { agentId: string; caseScore: Benchm {S.benchmark.colRun} #{i + 1} {formatScore(run.score)} - {formatMoney(run.cost)} + {formatMoney(run.cost, currency)} {run.durationMs !== undefined ? humanizeDuration(run.durationMs) : "—"} @@ -491,6 +516,7 @@ function CaseRow({ agentId, caseScore: c }: { agentId: string; caseScore: Benchm export function BenchmarkPage() { useDocumentTitle(S.benchmark.title); const { currentProject, agents, agentsLoading } = useProject(); + const { currency } = useTheme(); const projectId = currentProject?.projectId ?? null; // ?agentId= deep link (entered from the "Benchmark" tab on the Agent settings page): only the target Agent is expanded by default. const [searchParams] = useSearchParams(); @@ -555,7 +581,7 @@ export function BenchmarkPage() { ) : ( <> - +

@@ -575,7 +601,12 @@ export function BenchmarkPage() { {[...evaluations].reverse().map((ev, i) => ( - + ))}