fix(web): honor currency setting in benchmark costs (#113)
This commit is contained in:
1 parent
4681135423
commit
a4a434cfaf
1 file changed
+44
-13
@@ -25,6 +25,8 @@ import { apiErrorText } from "../../lib/api-error";
|
|||||||
import { useDocumentTitle } from "../../lib/use-document-title";
|
import { useDocumentTitle } from "../../lib/use-document-title";
|
||||||
import { formatDateTime, formatMoney, formatScore, humanizeDuration } from "../../lib/format";
|
import { formatDateTime, formatMoney, formatScore, humanizeDuration } from "../../lib/format";
|
||||||
import { agentDisplayName, useProject } from "../../state/project";
|
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 { AgentAvatar } from "../../components/ui/agent-avatar";
|
||||||
import { Chevron } from "../../components/ui/chevron";
|
import { Chevron } from "../../components/ui/chevron";
|
||||||
import { Segmented } from "../../components/ui/segmented";
|
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. */
|
/** 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"
|
return metric === "score"
|
||||||
? formatScore(v)
|
? formatScore(v)
|
||||||
: metric === "cost"
|
: metric === "cost"
|
||||||
? formatMoney(v)
|
? formatMoney(v, currency)
|
||||||
: humanizeDuration(v);
|
: humanizeDuration(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,10 +178,12 @@ function MetricTrendChart({
|
|||||||
evaluations,
|
evaluations,
|
||||||
series,
|
series,
|
||||||
metric,
|
metric,
|
||||||
|
currency,
|
||||||
}: {
|
}: {
|
||||||
evaluations: BenchmarkEvaluation[];
|
evaluations: BenchmarkEvaluation[];
|
||||||
series: EvaluationSeries[];
|
series: EvaluationSeries[];
|
||||||
metric: BenchmarkMetric;
|
metric: BenchmarkMetric;
|
||||||
|
currency: Currency;
|
||||||
}) {
|
}) {
|
||||||
const [hover, setHover] = useState<number | null>(null);
|
const [hover, setHover] = useState<number | null>(null);
|
||||||
const [ref, width] = useChartWidth();
|
const [ref, width] = useChartWidth();
|
||||||
@@ -194,7 +198,7 @@ function MetricTrendChart({
|
|||||||
{width > 0 && (
|
{width > 0 && (
|
||||||
<ChartFrame
|
<ChartFrame
|
||||||
geom={geom}
|
geom={geom}
|
||||||
fmtY={(v) => formatMetric(metric, v)}
|
fmtY={(v) => formatMetric(metric, v, currency)}
|
||||||
dates={dates}
|
dates={dates}
|
||||||
hover={hover}
|
hover={hover}
|
||||||
onHover={setHover}
|
onHover={setHover}
|
||||||
@@ -205,7 +209,7 @@ function MetricTrendChart({
|
|||||||
<>
|
<>
|
||||||
<p className="text-gray-400">{formatDateTime(e.time)}</p>
|
<p className="text-gray-400">{formatDateTime(e.time)}</p>
|
||||||
<p className="font-mono">
|
<p className="font-mono">
|
||||||
{v === null ? "—" : formatMetric(metric, v)}
|
{v === null ? "—" : formatMetric(metric, v, currency)}
|
||||||
{e.version !== undefined && (
|
{e.version !== undefined && (
|
||||||
<span className="ml-1.5 text-gray-400">v{e.version}</span>
|
<span className="ml-1.5 text-gray-400">v{e.version}</span>
|
||||||
)}
|
)}
|
||||||
@@ -277,7 +281,13 @@ function MetricTrendChart({
|
|||||||
* disambiguate. Mounted under a keyed container per Benchmark: switching Benchmarks resets back
|
* disambiguate. Mounted under a keyed container per Benchmark: switching Benchmarks resets back
|
||||||
* to "score".
|
* to "score".
|
||||||
*/
|
*/
|
||||||
function TrendSection({ evaluations }: { evaluations: BenchmarkEvaluation[] }) {
|
function TrendSection({
|
||||||
|
evaluations,
|
||||||
|
currency,
|
||||||
|
}: {
|
||||||
|
evaluations: BenchmarkEvaluation[];
|
||||||
|
currency: Currency;
|
||||||
|
}) {
|
||||||
const [metric, setMetric] = useState<BenchmarkMetric>("score");
|
const [metric, setMetric] = useState<BenchmarkMetric>("score");
|
||||||
const series = modelSeries(evaluations);
|
const series = modelSeries(evaluations);
|
||||||
const ids = series.map((s) => s.modelId).filter((v): v is string => v !== undefined);
|
const ids = series.map((s) => s.modelId).filter((v): v is string => v !== undefined);
|
||||||
@@ -324,7 +334,12 @@ function TrendSection({ evaluations }: { evaluations: BenchmarkEvaluation[] }) {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<MetricTrendChart evaluations={evaluations} series={series} metric={metric} />
|
<MetricTrendChart
|
||||||
|
evaluations={evaluations}
|
||||||
|
series={series}
|
||||||
|
metric={metric}
|
||||||
|
currency={currency}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -335,9 +350,11 @@ const CELL = "px-3 py-2";
|
|||||||
function EvaluationRow({
|
function EvaluationRow({
|
||||||
agentId,
|
agentId,
|
||||||
evaluation,
|
evaluation,
|
||||||
|
currency,
|
||||||
}: {
|
}: {
|
||||||
agentId: string;
|
agentId: string;
|
||||||
evaluation: BenchmarkEvaluation;
|
evaluation: BenchmarkEvaluation;
|
||||||
|
currency: Currency;
|
||||||
}) {
|
}) {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
return (
|
return (
|
||||||
@@ -365,7 +382,7 @@ function EvaluationRow({
|
|||||||
{formatScore(evaluation.score)}
|
{formatScore(evaluation.score)}
|
||||||
</td>
|
</td>
|
||||||
<td className={`${CELL} font-mono text-xs tabular-nums text-gray-500 dark:text-gray-400`}>
|
<td className={`${CELL} font-mono text-xs tabular-nums text-gray-500 dark:text-gray-400`}>
|
||||||
{formatMoney(evaluation.cost)}
|
{formatMoney(evaluation.cost, currency)}
|
||||||
</td>
|
</td>
|
||||||
<td className={`${CELL} font-mono text-xs tabular-nums text-gray-500 dark:text-gray-400`}>
|
<td className={`${CELL} font-mono text-xs tabular-nums text-gray-500 dark:text-gray-400`}>
|
||||||
{evaluation.durationMs !== undefined ? humanizeDuration(evaluation.durationMs) : "—"}
|
{evaluation.durationMs !== undefined ? humanizeDuration(evaluation.durationMs) : "—"}
|
||||||
@@ -406,7 +423,7 @@ function EvaluationRow({
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{evaluation.cases.map((c) => (
|
{evaluation.cases.map((c) => (
|
||||||
<CaseRow key={c.case} agentId={agentId} caseScore={c} />
|
<CaseRow key={c.case} agentId={agentId} caseScore={c} currency={currency} />
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -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
|
* 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.
|
* 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 [open, setOpen] = useState(false);
|
||||||
const runs = c.runs ?? [];
|
const runs = c.runs ?? [];
|
||||||
const expandable = runs.length > 0;
|
const expandable = runs.length > 0;
|
||||||
@@ -455,7 +480,7 @@ function CaseRow({ agentId, caseScore: c }: { agentId: string; caseScore: Benchm
|
|||||||
</td>
|
</td>
|
||||||
<td className="px-2 py-1 font-mono tabular-nums">{formatScore(c.score)}</td>
|
<td className="px-2 py-1 font-mono tabular-nums">{formatScore(c.score)}</td>
|
||||||
<td className="px-2 py-1 font-mono tabular-nums text-gray-500 dark:text-gray-400">
|
<td className="px-2 py-1 font-mono tabular-nums text-gray-500 dark:text-gray-400">
|
||||||
{formatMoney(c.cost)}
|
{formatMoney(c.cost, currency)}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-2 py-1 font-mono tabular-nums text-gray-500 dark:text-gray-400">
|
<td className="px-2 py-1 font-mono tabular-nums text-gray-500 dark:text-gray-400">
|
||||||
{c.durationMs !== undefined ? humanizeDuration(c.durationMs) : "—"}
|
{c.durationMs !== undefined ? humanizeDuration(c.durationMs) : "—"}
|
||||||
@@ -472,7 +497,7 @@ function CaseRow({ agentId, caseScore: c }: { agentId: string; caseScore: Benchm
|
|||||||
{S.benchmark.colRun} #{i + 1}
|
{S.benchmark.colRun} #{i + 1}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-2 py-1 font-mono tabular-nums">{formatScore(run.score)}</td>
|
<td className="px-2 py-1 font-mono tabular-nums">{formatScore(run.score)}</td>
|
||||||
<td className="px-2 py-1 font-mono tabular-nums">{formatMoney(run.cost)}</td>
|
<td className="px-2 py-1 font-mono tabular-nums">{formatMoney(run.cost, currency)}</td>
|
||||||
<td className="px-2 py-1 font-mono tabular-nums">
|
<td className="px-2 py-1 font-mono tabular-nums">
|
||||||
{run.durationMs !== undefined ? humanizeDuration(run.durationMs) : "—"}
|
{run.durationMs !== undefined ? humanizeDuration(run.durationMs) : "—"}
|
||||||
</td>
|
</td>
|
||||||
@@ -491,6 +516,7 @@ function CaseRow({ agentId, caseScore: c }: { agentId: string; caseScore: Benchm
|
|||||||
export function BenchmarkPage() {
|
export function BenchmarkPage() {
|
||||||
useDocumentTitle(S.benchmark.title);
|
useDocumentTitle(S.benchmark.title);
|
||||||
const { currentProject, agents, agentsLoading } = useProject();
|
const { currentProject, agents, agentsLoading } = useProject();
|
||||||
|
const { currency } = useTheme();
|
||||||
const projectId = currentProject?.projectId ?? null;
|
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.
|
// ?agentId= deep link (entered from the "Benchmark" tab on the Agent settings page): only the target Agent is expanded by default.
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
@@ -555,7 +581,7 @@ export function BenchmarkPage() {
|
|||||||
<EmptyState title={S.benchmark.noEvaluations} />
|
<EmptyState title={S.benchmark.noEvaluations} />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<TrendSection evaluations={evaluations} />
|
<TrendSection evaluations={evaluations} currency={currency} />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<p className="mb-1 text-xs font-semibold text-gray-500">
|
<p className="mb-1 text-xs font-semibold text-gray-500">
|
||||||
@@ -575,7 +601,12 @@ export function BenchmarkPage() {
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{[...evaluations].reverse().map((ev, i) => (
|
{[...evaluations].reverse().map((ev, i) => (
|
||||||
<EvaluationRow key={i} agentId={selection.agentId} evaluation={ev} />
|
<EvaluationRow
|
||||||
|
key={i}
|
||||||
|
agentId={selection.agentId}
|
||||||
|
evaluation={ev}
|
||||||
|
currency={currency}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
Reference in new issue
Block a user