'use client'; import React, { useEffect, useState } from "react"; import { normalizeBenchmarkName, computeAverageScore, } from "./util"; import {useTranslation} from "react-i18next"; import {useParams} from "next/navigation"; import {BACKEND_ADDRESS} from "@/app/resources/ResourcesPaths"; const allowedMetrics = [ 'acc', 'accuracy', 'f1', 'pearson', 'pearsonr', 'spearman', ]; export default function LeaderboardPage() { const { t } = useTranslation(); const { id: _ } = useParams(); // unused here const [entries, setEntries] = useState([]); const [benchmarks, setBenchmarks] = useState([]); const [sortCol, setSortCol] = useState('overall'); const [sortOrder, setSortOrder] = useState('desc'); const [selectedEntry, setSelectedEntry] = useState(null); // Build header labels with translations const headerLabels = { model: t('leaderboard_modelHeader'), overall: t('leaderboard_overallHeader'), }; useEffect(() => { fetch(`${BACKEND_ADDRESS}/leaderboard`) .then((res) => { if (!res.ok) throw new Error(`HTTP ${res.status}`); return res.json(); }) .then((data) => { const withOverall = data.map((e) => ({ ...e, averageScore: computeAverageScore(e), })); setEntries(withOverall); const allBench = new Set(); withOverall.forEach((entry) => { Object.keys(entry.results || {}).forEach((raw) => { allBench.add(normalizeBenchmarkName(raw)); }); }); setBenchmarks(Array.from(allBench)); }) .catch((err) => console.error('Failed to load leaderboard:', err)); }, []); const getCellValue = (entry, col) => { if (col === 'model') return entry.display_name; if (col === 'overall') return entry.averageScore ?? null; const pair = Object.entries(entry.results || {}).find( ([rawName]) => normalizeBenchmarkName(rawName) === col ); if (!pair) return null; const rawValues = []; Object.values(pair[1]).forEach((metricGroup) => { if (metricGroup && typeof metricGroup === 'object') { Object.entries(metricGroup).forEach(([metricName, metricValue]) => { if ( !metricName.includes('_warning') && typeof metricValue === 'number' && allowedMetrics.includes(metricName.toLowerCase()) ) { rawValues.push(metricValue); } }); } }); if (rawValues.length === 0) return null; const normalized = rawValues.map((v) => (v > 1 ? v / 100 : v)); return normalized.reduce((a, b) => a + b, 0) / normalized.length; }; const sorted = [...entries].sort((a, b) => { const va = getCellValue(a, sortCol); const vb = getCellValue(b, sortCol); if (sortCol === 'model') { if (va == null) return 1; if (vb == null) return -1; return sortOrder === 'asc' ? va.localeCompare(vb) : vb.localeCompare(va); } const na = va ?? -Infinity; const nb = vb ?? -Infinity; return sortOrder === 'asc' ? na - nb : nb - na; }); const handleSort = (col) => { if (sortCol === col) { setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc'); } else { setSortCol(col); setSortOrder('desc'); } }; const renderHeader = (col) => { const baseLabel = headerLabels[col] ?? col; const arrow = sortCol === col ? (sortOrder === 'asc' ? ' ▲' : ' ▼') : ''; if (col === 'overall') { return (
{renderHeader(b)} | ))}||
---|---|---|
{entry.display_name} | {entry.averageScore == null ? t('leaderboard_notSpecified') : (entry.averageScore * 100).toFixed(1) + '%'} | {benchmarks.map((b) => { const val = getCellValue(entry, b); return ({val == null ? t('leaderboard_notSpecified') : (val * 100).toFixed(1) + '%'} | ); })}
⚠️ {values[`${metricType}_warning`]}
)}