Spaces:
Running
Running
| const ACCENT = "#5eead4"; | |
| const BASE = "#6b7280"; | |
| const css = (v) => getComputedStyle(document.documentElement).getPropertyValue(v).trim(); | |
| let DATA = null; | |
| let scoreChart = null; | |
| let lenChart = null; | |
| let loading = false; | |
| const $ = (id) => document.getElementById(id); | |
| function shortKey(k) { | |
| if (!k || k.length <= 14) return { head: k || "pending", tail: "" }; | |
| return { head: k.slice(0, 8), tail: "…" + k.slice(-6) }; | |
| } | |
| async function load() { | |
| await refreshData(true); | |
| window.setInterval(() => refreshData(false), 15000); | |
| } | |
| async function refreshData(initial) { | |
| if (loading) return; | |
| loading = true; | |
| const previousView = $("validator-select")?.value; | |
| try { | |
| const res = await fetch("./data.json", { cache: "no-store" }); | |
| if (!res.ok) throw new Error("HTTP " + res.status); | |
| DATA = await res.json(); | |
| } catch (err) { | |
| console.error("Failed to load data.json", err); | |
| if (initial || !DATA) { | |
| $("board-empty").hidden = false; | |
| $("board-empty").textContent = "Data is not ready yet"; | |
| } | |
| loading = false; | |
| return; | |
| } | |
| hydrateUpdated(); | |
| buildViews(previousView); | |
| renderActiveView(); | |
| loading = false; | |
| } | |
| function hydrateUpdated() { | |
| const ts = DATA.generated_at; | |
| const date = ts ? new Date(ts) : null; | |
| const label = date && !Number.isNaN(date.getTime()) ? formatTimestamp(date) : "unknown"; | |
| $("updated").textContent = "latest " + label; | |
| $("footer-meta").textContent = DATA.sample ? "sample data" : "live data"; | |
| } | |
| function formatTimestamp(date) { | |
| const day = date.toLocaleDateString(undefined, { year: "2-digit", month: "numeric", day: "numeric" }); | |
| const time = date.toLocaleTimeString(undefined, { hour: "numeric", minute: "2-digit" }); | |
| return `${day} ${time}`; | |
| } | |
| function buildViews(preferredView) { | |
| const select = $("validator-select"); | |
| select.innerHTML = ""; | |
| const views = []; | |
| for (const v of DATA.validators || []) { | |
| const sk = shortKey(v.hotkey); | |
| views.push({ id: v.hotkey, label: "Validator " + sk.head + sk.tail }); | |
| } | |
| for (const view of views) { | |
| const opt = document.createElement("option"); | |
| opt.value = view.id; | |
| opt.textContent = view.label; | |
| select.appendChild(opt); | |
| } | |
| if (preferredView && views.some((view) => view.id === preferredView)) { | |
| select.value = preferredView; | |
| } | |
| select.onchange = renderActiveView; | |
| $("stat-validators").textContent = (DATA.validators || []).length || "0"; | |
| } | |
| const FULL_EVALUATION_ENTRANT_STATES = new Set(["evaluating", "finished", "failed"]); | |
| function roundParticipation(progresses) { | |
| const miners = new Map(); | |
| let hasCurrentRound = false; | |
| for (const progress of progresses) { | |
| for (const [stageName, stage] of Object.entries(progress?.stages || {})) { | |
| if (stageName !== "qualification" && stageName !== "full_evaluation") continue; | |
| const entries = Object.entries(stage?.miners || {}); | |
| if (entries.length) hasCurrentRound = true; | |
| for (const [hotkey, status] of entries) { | |
| const participation = miners.get(hotkey) || { | |
| qualification: false, | |
| fullEvaluation: false, | |
| qualificationStatus: null, | |
| fullEvaluationStatus: null, | |
| }; | |
| if (stageName === "qualification") { | |
| participation.qualification = true; | |
| participation.qualificationStatus = status; | |
| } | |
| if (stageName === "full_evaluation" && FULL_EVALUATION_ENTRANT_STATES.has(status)) { | |
| participation.fullEvaluation = true; | |
| } | |
| if (stageName === "full_evaluation") participation.fullEvaluationStatus = status; | |
| miners.set(hotkey, participation); | |
| } | |
| } | |
| } | |
| return { miners, hasCurrentRound }; | |
| } | |
| function numericScore(value) { | |
| return typeof value === "number" && Number.isFinite(value) ? value : null; | |
| } | |
| function currentRoundRows(progress) { | |
| const round = roundParticipation(progress ? [progress] : []); | |
| const qualificationScores = progress?.stages?.qualification?.scores || {}; | |
| const fullEvaluationScores = progress?.stages?.full_evaluation?.scores || {}; | |
| const rows = [...round.miners].map(([hotkey, participation]) => ({ | |
| hotkey, | |
| participation, | |
| qualificationScore: numericScore(qualificationScores[hotkey]), | |
| fullEvaluationScore: numericScore(fullEvaluationScores[hotkey]), | |
| })); | |
| rows.sort((a, b) => { | |
| if (a.fullEvaluationScore != null || b.fullEvaluationScore != null) { | |
| if (a.fullEvaluationScore == null) return 1; | |
| if (b.fullEvaluationScore == null) return -1; | |
| return b.fullEvaluationScore - a.fullEvaluationScore; | |
| } | |
| if (a.qualificationScore == null) { | |
| return b.qualificationScore == null ? a.hotkey.localeCompare(b.hotkey) : 1; | |
| } | |
| if (b.qualificationScore == null) return -1; | |
| return b.qualificationScore - a.qualificationScore; | |
| }); | |
| return { rows, hasCurrentRound: round.hasCurrentRound, hotkeys: new Set(round.miners.keys()) }; | |
| } | |
| function scoreFromHistory(history, hotkey, epoch) { | |
| const epochs = history?.epochs || []; | |
| const index = epochs.indexOf(epoch); | |
| if (index < 0) return null; | |
| return numericScore(history?.miners?.[hotkey]?.score?.[index]); | |
| } | |
| function stageScoresFromHistory(history, epoch) { | |
| const snapshot = (history?.stage_scores || []).find((row) => row.epoch === epoch); | |
| return { | |
| qualification: snapshot?.qualification || {}, | |
| fullEvaluation: snapshot?.full_evaluation || {}, | |
| }; | |
| } | |
| function previousRoundRows(rankings, history, progress, currentHotkeys) { | |
| const epochs = history?.epochs || []; | |
| const currentEpoch = numericScore(progress?.epoch); | |
| const previousEpoch = currentEpoch == null | |
| ? (epochs.length ? epochs[epochs.length - 1] : null) | |
| : [...epochs].reverse().find((epoch) => epoch < currentEpoch) ?? null; | |
| const latestCompletedEpoch = epochs.length ? epochs[epochs.length - 1] : null; | |
| const finalScores = new Map(); | |
| if (previousEpoch != null) { | |
| for (const hotkey of Object.keys(history?.miners || {})) { | |
| const score = scoreFromHistory(history, hotkey, previousEpoch); | |
| if (score != null) finalScores.set(hotkey, score); | |
| } | |
| } | |
| if (previousEpoch == null && latestCompletedEpoch == null) { | |
| for (const row of rankings) { | |
| const score = numericScore(row.score); | |
| if (score != null && !finalScores.has(row.hotkey)) finalScores.set(row.hotkey, score); | |
| } | |
| } | |
| const stageScores = stageScoresFromHistory(history, previousEpoch); | |
| const hotkeys = new Set([ | |
| ...finalScores.keys(), | |
| ...Object.keys(stageScores.qualification), | |
| ...Object.keys(stageScores.fullEvaluation), | |
| ]); | |
| const rows = [...hotkeys].map((hotkey) => ({ | |
| hotkey, | |
| finalScore: finalScores.get(hotkey) ?? null, | |
| qualificationScore: numericScore(stageScores.qualification[hotkey]), | |
| fullEvaluationScore: numericScore(stageScores.fullEvaluation[hotkey]), | |
| isCurrent: currentHotkeys.has(hotkey), | |
| })); | |
| rows.sort((a, b) => { | |
| if (a.finalScore == null) return b.finalScore == null ? a.hotkey.localeCompare(b.hotkey) : 1; | |
| if (b.finalScore == null) return -1; | |
| return b.finalScore - a.finalScore; | |
| }); | |
| rows.forEach((row, index) => { row.previousRank = row.finalScore == null ? null : index + 1; }); | |
| return { rows, epoch: previousEpoch }; | |
| } | |
| function olderMinerRows(history, currentHotkeys, previousRows, previousEpoch) { | |
| if (previousEpoch == null) return []; | |
| const epochs = history?.epochs || []; | |
| const excluded = new Set([...currentHotkeys, ...previousRows.map((row) => row.hotkey)]); | |
| const rows = []; | |
| for (const [hotkey, series] of Object.entries(history?.miners || {})) { | |
| if (excluded.has(hotkey)) continue; | |
| let index = epochs.length - 1; | |
| while (index >= 0 && (epochs[index] >= previousEpoch || numericScore(series?.score?.[index]) == null)) { | |
| index -= 1; | |
| } | |
| if (index < 0) continue; | |
| const epoch = epochs[index]; | |
| const stageScores = stageScoresFromHistory(history, epoch); | |
| rows.push({ | |
| hotkey, | |
| lastScoredEpoch: epoch, | |
| finalScore: numericScore(series.score[index]), | |
| qualificationScore: numericScore(stageScores.qualification[hotkey]), | |
| fullEvaluationScore: numericScore(stageScores.fullEvaluation[hotkey]), | |
| }); | |
| } | |
| rows.sort((a, b) => ( | |
| b.lastScoredEpoch - a.lastScoredEpoch | |
| || (b.finalScore ?? -Infinity) - (a.finalScore ?? -Infinity) | |
| )); | |
| return rows; | |
| } | |
| function activeView() { | |
| const id = $("validator-select").value; | |
| const v = (DATA.validators || []).find((x) => x.hotkey === id); | |
| const current = currentRoundRows(v?.progress || null); | |
| const previous = previousRoundRows( | |
| v?.rankings || [], | |
| v?.history || null, | |
| v?.progress || null, | |
| current.hotkeys, | |
| ); | |
| const older = olderMinerRows(v?.history || null, current.hotkeys, previous.rows, previous.epoch); | |
| return { | |
| rankings: current.rows, | |
| previousRankings: previous.rows, | |
| previousEpoch: previous.epoch, | |
| olderRankings: older, | |
| hasCurrentRound: current.hasCurrentRound, | |
| history: v?.history || null, | |
| progress: v?.progress || null, | |
| validator: v || null, | |
| }; | |
| } | |
| function renderActiveView() { | |
| const view = activeView(); | |
| renderBoard(view); | |
| renderStats(view); | |
| renderProgress(view); | |
| renderTrends(view); | |
| } | |
| function renderStats(view) { | |
| const top = view.rankings.find((row) => row.fullEvaluationScore != null); | |
| $("stat-miners").textContent = view.rankings.length; | |
| $("stat-top").textContent = top ? top.fullEvaluationScore.toFixed(3) : "-"; | |
| const ep = view.history?.epochs; | |
| $("stat-epoch").textContent = view.progress?.epoch ?? (ep && ep.length ? ep[ep.length - 1] : "0"); | |
| } | |
| const TERMINAL_MINER_STATES = new Set(["finished", "failed", "rejected", "skipped", "not_selected"]); | |
| function statusLabel(status) { | |
| return String(status || "waiting").replaceAll("_", " "); | |
| } | |
| function statusClass(status) { | |
| const safe = String(status || "waiting").toLowerCase().replaceAll("_", "-").replace(/[^a-z-]/g, ""); | |
| return "status-" + safe; | |
| } | |
| function compactStatusLabel(status) { | |
| switch (status) { | |
| case "completed": | |
| case "finished": | |
| return "done"; | |
| case "evaluating": | |
| case "running": | |
| return "live"; | |
| case "not_selected": | |
| return "out"; | |
| default: | |
| return statusLabel(status || "waiting"); | |
| } | |
| } | |
| function appendHotkey(parent, value) { | |
| const sk = shortKey(value); | |
| const hotkey = document.createElement("span"); | |
| hotkey.className = "hotkey"; | |
| const head = document.createElement("span"); | |
| head.className = "head"; | |
| head.textContent = sk.head; | |
| const tail = document.createElement("span"); | |
| tail.className = "tail"; | |
| tail.textContent = sk.tail; | |
| hotkey.append(head, tail); | |
| parent.appendChild(hotkey); | |
| } | |
| function progressTimestamp(progress) { | |
| const date = progress?.updated_at ? new Date(progress.updated_at) : null; | |
| return date && !Number.isNaN(date.getTime()) ? date : null; | |
| } | |
| function displayProgressStatus(progress) { | |
| return progress?.status || "waiting"; | |
| } | |
| function stageStats(stage) { | |
| const entries = Object.entries(stage?.miners || {}); | |
| const resolved = entries.filter(([, status]) => TERMINAL_MINER_STATES.has(status)).length; | |
| const finished = entries.filter(([, status]) => status === "finished").length; | |
| const evaluating = entries.filter(([, status]) => status === "evaluating").length; | |
| return { total: entries.length, resolved, finished, evaluating }; | |
| } | |
| function stageProgressPercent(stats) { | |
| return stats.total ? Math.round((stats.resolved / stats.total) * 100) : 0; | |
| } | |
| function baselineStatus(stage) { | |
| return stage?.baseline || "waiting"; | |
| } | |
| function stageScore(stage, hotkey) { | |
| const score = stage?.scores?.[hotkey]; | |
| return typeof score === "number" && Number.isFinite(score) ? score : null; | |
| } | |
| function stageTopScore(stage) { | |
| const scores = Object.values(stage?.scores || {}).filter( | |
| (value) => typeof value === "number" && Number.isFinite(value), | |
| ); | |
| return scores.length ? Math.max(...scores) : null; | |
| } | |
| function formatScore(value) { | |
| return value == null ? "-" : value.toFixed(3); | |
| } | |
| function formatDuration(seconds) { | |
| if (typeof seconds !== "number" || !Number.isFinite(seconds) || seconds < 0) return null; | |
| if (seconds < 60) return `${seconds.toFixed(1)}s`; | |
| const minutes = Math.floor(seconds / 60); | |
| const rest = Math.round(seconds % 60); | |
| return `${minutes}m ${String(rest).padStart(2, "0")}s`; | |
| } | |
| function baselineDurationLabel(stage) { | |
| return formatDuration(stage?.baseline_seconds); | |
| } | |
| function stageDurationLabel(stage) { | |
| return formatDuration(stage?.duration_seconds); | |
| } | |
| function stageIsEvaluating(stage, stats = stageStats(stage)) { | |
| return stage?.status === "evaluating" || baselineStatus(stage) === "evaluating" || stats.evaluating > 0; | |
| } | |
| function renderProgress(view) { | |
| const content = $("progress-content"); | |
| content.innerHTML = ""; | |
| if (!view.validator) { | |
| renderProgressOverview(content); | |
| return; | |
| } | |
| const progress = view.progress; | |
| const validatorKey = shortKey(view.validator.hotkey); | |
| $("progress-subtitle").textContent = `Epoch ${progress?.epoch ?? "-"} from validator ${validatorKey.head}${validatorKey.tail}`; | |
| const overallStatus = displayProgressStatus(progress); | |
| $("progress-status").textContent = statusLabel(overallStatus); | |
| $("progress-status").className = `status-pill ${statusClass(overallStatus)}`; | |
| const updated = progressTimestamp(progress); | |
| $("progress-updated").textContent = updated ? formatTimestamp(updated) : "syncing"; | |
| if (!progress) { | |
| const empty = document.createElement("div"); | |
| empty.className = "empty"; | |
| empty.textContent = "Waiting for this validator"; | |
| content.appendChild(empty); | |
| return; | |
| } | |
| const grid = document.createElement("div"); | |
| grid.className = "stage-grid"; | |
| grid.append( | |
| renderStageCard("Qualification", progress.stages?.qualification), | |
| renderStageCard("Full evaluation", progress.stages?.full_evaluation), | |
| ); | |
| content.appendChild(grid); | |
| } | |
| function renderProgressOverview(content) { | |
| $("progress-subtitle").textContent = "Live stage health across validators"; | |
| $("progress-status").textContent = "all"; | |
| $("progress-status").className = "status-pill"; | |
| const validators = DATA.validators || []; | |
| const dates = validators.map((validator) => progressTimestamp(validator.progress)).filter(Boolean); | |
| const latest = dates.length ? new Date(Math.max(...dates.map((date) => date.getTime()))) : null; | |
| $("progress-updated").textContent = latest ? formatTimestamp(latest) : "syncing"; | |
| if (!validators.length) { | |
| const empty = document.createElement("div"); | |
| empty.className = "empty"; | |
| empty.textContent = "Waiting for validator progress"; | |
| content.appendChild(empty); | |
| return; | |
| } | |
| const overview = document.createElement("div"); | |
| overview.className = "progress-overview"; | |
| for (const validator of validators) overview.appendChild(renderValidatorProgressRow(validator)); | |
| content.appendChild(overview); | |
| } | |
| function renderValidatorProgressRow(validator) { | |
| const row = document.createElement("div"); | |
| row.className = "validator-progress-row"; | |
| const name = document.createElement("div"); | |
| name.className = "validator-progress-name"; | |
| appendHotkey(name, validator.hotkey); | |
| const status = displayProgressStatus(validator.progress); | |
| const pill = document.createElement("span"); | |
| pill.className = `status-pill ${statusClass(status)}`; | |
| pill.textContent = statusLabel(status); | |
| name.appendChild(pill); | |
| row.append( | |
| name, | |
| renderStageSummary("Qualification", validator.progress?.stages?.qualification), | |
| renderStageSummary("Full evaluation", validator.progress?.stages?.full_evaluation), | |
| ); | |
| return row; | |
| } | |
| function renderStageSummary(label, stage) { | |
| const summary = document.createElement("div"); | |
| const stats = stageStats(stage); | |
| summary.className = stageIsEvaluating(stage, stats) ? "stage-summary is-evaluating" : "stage-summary"; | |
| const baseDuration = baselineDurationLabel(stage); | |
| summary.title = stage | |
| ? `${label} ${statusLabel(stage.status)} with base ${statusLabel(baselineStatus(stage))}` + | |
| (baseDuration ? ` (${baseDuration})` : "") + | |
| ` and ${stats.resolved}/${stats.total} miners complete` | |
| : `${label} waiting for data`; | |
| const head = document.createElement("div"); | |
| head.className = "stage-summary-head"; | |
| const title = document.createElement("strong"); | |
| title.textContent = label; | |
| const state = document.createElement("span"); | |
| state.className = `stage-state ${statusClass(stage?.status)}`; | |
| state.textContent = compactStatusLabel(stage?.status); | |
| head.append(title, state); | |
| const meter = document.createElement("div"); | |
| meter.className = "stage-mini-bar"; | |
| const fill = document.createElement("i"); | |
| fill.style.width = `${stageProgressPercent(stats)}%`; | |
| meter.appendChild(fill); | |
| const foot = document.createElement("div"); | |
| foot.className = "stage-summary-foot"; | |
| const done = document.createElement("span"); | |
| done.className = "stage-mini-count"; | |
| done.textContent = stage ? `${stats.resolved}/${stats.total}` : "0/0"; | |
| done.setAttribute("aria-label", "miners complete"); | |
| const baseline = document.createElement("span"); | |
| const baseStatus = baselineStatus(stage); | |
| baseline.className = `stage-mini-base ${statusClass(baseStatus)}`; | |
| baseline.textContent = `base ${compactStatusLabel(baseStatus)}` + (baseDuration ? ` · ${baseDuration}` : ""); | |
| baseline.title = `Base model ${statusLabel(baseStatus)}` + (baseDuration ? ` in ${baseDuration}` : ""); | |
| foot.append(done, baseline); | |
| if (stats.evaluating) { | |
| const active = document.createElement("span"); | |
| active.className = "stage-mini-active"; | |
| active.textContent = `${stats.evaluating} active`; | |
| foot.appendChild(active); | |
| } | |
| summary.append(head, meter, foot); | |
| return summary; | |
| } | |
| function renderStageCard(label, stage) { | |
| const card = document.createElement("section"); | |
| const stats = stageStats(stage); | |
| const isEvaluating = stageIsEvaluating(stage, stats); | |
| card.className = isEvaluating ? "stage-card is-evaluating" : "stage-card"; | |
| const head = document.createElement("div"); | |
| head.className = "stage-head"; | |
| const titleRow = document.createElement("div"); | |
| titleRow.className = "stage-title-row"; | |
| const title = document.createElement("span"); | |
| title.className = "stage-title"; | |
| title.textContent = label; | |
| const pill = document.createElement("span"); | |
| pill.className = `status-pill ${statusClass(stage?.status)}`; | |
| pill.textContent = statusLabel(stage?.status); | |
| const stageDuration = stageDurationLabel(stage); | |
| if (stageDuration) pill.title = `took ${stageDuration}`; | |
| titleRow.append(title, pill); | |
| const count = document.createElement("div"); | |
| const bar = document.createElement("div"); | |
| count.className = "stage-metrics"; | |
| const baseDuration = baselineDurationLabel(stage); | |
| count.append( | |
| renderStageMetric("done", `${stats.resolved}/${stats.total}`), | |
| renderStageMetric("active", String(stats.evaluating)), | |
| renderStageMetric( | |
| "base", | |
| compactStatusLabel(baselineStatus(stage)) + (baseDuration ? ` · ${baseDuration}` : ""), | |
| ), | |
| renderStageMetric("top", formatScore(stageTopScore(stage))), | |
| ); | |
| bar.className = isEvaluating ? "stage-bar is-evaluating" : "stage-bar"; | |
| const fill = document.createElement("i"); | |
| fill.style.width = `${stageProgressPercent(stats)}%`; | |
| bar.appendChild(fill); | |
| head.append(titleRow, count, bar); | |
| card.appendChild(head); | |
| const list = document.createElement("div"); | |
| list.className = "miner-state-list"; | |
| if (stage) { | |
| const baselineRow = document.createElement("div"); | |
| baselineRow.className = "miner-state-row baseline-state-row"; | |
| const label = document.createElement("span"); | |
| label.className = "baseline-label"; | |
| label.textContent = "Base model"; | |
| const state = document.createElement("span"); | |
| const baseStatus = baselineStatus(stage); | |
| const baseDuration = baselineDurationLabel(stage); | |
| state.className = `miner-state ${statusClass(baseStatus)}`; | |
| state.textContent = statusLabel(baseStatus) + (baseDuration ? ` · ${baseDuration}` : ""); | |
| baselineRow.append(label, state); | |
| list.appendChild(baselineRow); | |
| } | |
| const miners = Object.entries(stage?.miners || {}); | |
| if (!miners.length) { | |
| const empty = document.createElement("div"); | |
| empty.className = "empty"; | |
| empty.textContent = "No miners yet"; | |
| list.appendChild(empty); | |
| } else { | |
| for (const [hotkey, minerStatus] of miners) { | |
| const row = document.createElement("div"); | |
| row.className = "miner-state-row"; | |
| appendHotkey(row, hotkey); | |
| const score = stageScore(stage, hotkey); | |
| const state = document.createElement("span"); | |
| state.className = `miner-state ${statusClass(minerStatus)}`; | |
| state.textContent = statusLabel(minerStatus) + (score != null ? ` · ${formatScore(score)}` : ""); | |
| row.appendChild(state); | |
| list.appendChild(row); | |
| } | |
| } | |
| card.appendChild(list); | |
| return card; | |
| } | |
| function renderStageMetric(label, value) { | |
| const metric = document.createElement("span"); | |
| metric.className = `stage-metric stage-metric-${label}`; | |
| const key = document.createElement("span"); | |
| key.textContent = label; | |
| const val = document.createElement("strong"); | |
| val.textContent = value; | |
| metric.append(key, val); | |
| return metric; | |
| } | |
| function participationLabel(participation) { | |
| if (!participation) return "Latest ranking"; | |
| if (participation.qualification && participation.fullEvaluation) return "Qualification + full"; | |
| if (participation.fullEvaluation) return "Full evaluation"; | |
| return "Qualification"; | |
| } | |
| function renderParticipation(participation) { | |
| const badge = document.createElement("span"); | |
| badge.className = "entry-badge"; | |
| if (participation?.fullEvaluation) badge.classList.add("entry-full"); | |
| badge.textContent = participationLabel(participation); | |
| return badge; | |
| } | |
| function stageScoreText(score, status) { | |
| if (score != null) return score.toFixed(4); | |
| if (status === "not_selected") return "not selected"; | |
| if (status === "failed" || status === "rejected" || status === "skipped") return statusLabel(status); | |
| if (status === "evaluating") return "evaluating"; | |
| return "pending"; | |
| } | |
| function renderScoreCell(score, status, className) { | |
| const cell = document.createElement("td"); | |
| cell.className = `score-val ${className}`; | |
| cell.textContent = stageScoreText(score, status); | |
| if (score == null) cell.classList.add("score-pending"); | |
| return cell; | |
| } | |
| function renderBoard(view) { | |
| const rankings = view.rankings; | |
| const body = $("board-body"); | |
| body.innerHTML = ""; | |
| $("board-empty").hidden = rankings.length > 0; | |
| $("ranking-subtitle").textContent = view.hasCurrentRound | |
| ? `Stage scores from epoch ${view.progress?.epoch ?? "-"}` | |
| : "Waiting for current-round participation"; | |
| let displayedRank = 0; | |
| rankings.forEach((r) => { | |
| const tr = document.createElement("tr"); | |
| const hasFullScore = r.fullEvaluationScore != null; | |
| if (hasFullScore) displayedRank += 1; | |
| tr.className = hasFullScore ? "rank-" + displayedRank : "rank-pending"; | |
| if (displayedRank === 1 && hasFullScore) tr.classList.add("is-king"); | |
| const sk = shortKey(r.hotkey); | |
| const rankCell = document.createElement("td"); | |
| rankCell.className = "c-rank"; | |
| const rank = document.createElement("span"); | |
| rank.className = "rank-num"; | |
| rank.textContent = hasFullScore ? String(displayedRank).padStart(2, "0") : "--"; | |
| rankCell.appendChild(rank); | |
| const hotkeyCell = document.createElement("td"); | |
| const hotkey = document.createElement("span"); | |
| hotkey.className = "hotkey"; | |
| const head = document.createElement("span"); | |
| head.className = "head"; | |
| head.textContent = sk.head; | |
| const tail = document.createElement("span"); | |
| tail.className = "tail"; | |
| tail.textContent = sk.tail; | |
| hotkey.append(head, tail); | |
| hotkeyCell.appendChild(hotkey); | |
| if (displayedRank === 1 && hasFullScore) { | |
| const king = document.createElement("span"); | |
| king.className = "king-badge"; | |
| king.textContent = "\u265B King"; | |
| hotkeyCell.appendChild(king); | |
| tr.setAttribute("aria-label", `King: ${r.hotkey}`); | |
| } | |
| const stageCell = document.createElement("td"); | |
| stageCell.className = "c-stage"; | |
| stageCell.appendChild(renderParticipation(r.participation)); | |
| const qualificationCell = renderScoreCell( | |
| r.qualificationScore, | |
| r.participation?.qualificationStatus, | |
| "c-qualification", | |
| ); | |
| const fullEvaluationCell = renderScoreCell( | |
| r.fullEvaluationScore, | |
| r.participation?.fullEvaluationStatus, | |
| "c-full-evaluation", | |
| ); | |
| tr.append(rankCell, hotkeyCell, stageCell, qualificationCell, fullEvaluationCell); | |
| body.appendChild(tr); | |
| }); | |
| renderPreviousBoard(view.previousRankings, view.previousEpoch); | |
| renderOlderBoard(view.olderRankings); | |
| } | |
| function renderPreviousBoard(rankings, epoch) { | |
| const section = $("previous-rankings"); | |
| const body = $("previous-board-body"); | |
| body.innerHTML = ""; | |
| section.hidden = !rankings.length; | |
| $("previous-count").textContent = epoch == null | |
| ? `${rankings.length} miners` | |
| : `${rankings.length} miners · epoch ${epoch}`; | |
| for (const row of rankings) { | |
| const tr = document.createElement("tr"); | |
| const rankCell = document.createElement("td"); | |
| rankCell.className = "c-rank"; | |
| rankCell.textContent = row.previousRank == null ? "--" : String(row.previousRank).padStart(2, "0"); | |
| const hotkeyCell = document.createElement("td"); | |
| appendHotkey(hotkeyCell, row.hotkey); | |
| const statusCell = document.createElement("td"); | |
| statusCell.className = "c-round-status"; | |
| const status = document.createElement("span"); | |
| status.className = row.isCurrent ? "entry-badge" : "status-pill status-stale"; | |
| status.textContent = row.isCurrent ? "also current" : "stale"; | |
| statusCell.appendChild(status); | |
| const qualificationCell = renderScoreCell(row.qualificationScore, null, "c-qualification"); | |
| const fullEvaluationCell = renderScoreCell(row.fullEvaluationScore, null, "c-full-evaluation"); | |
| const finalScoreCell = renderScoreCell(row.finalScore, null, "c-final-score"); | |
| if (row.qualificationScore == null) qualificationCell.textContent = "-"; | |
| if (row.fullEvaluationScore == null) fullEvaluationCell.textContent = "-"; | |
| if (row.finalScore == null) finalScoreCell.textContent = "-"; | |
| tr.append( | |
| rankCell, | |
| hotkeyCell, | |
| statusCell, | |
| qualificationCell, | |
| fullEvaluationCell, | |
| finalScoreCell, | |
| ); | |
| body.appendChild(tr); | |
| } | |
| } | |
| function renderOlderBoard(rankings) { | |
| const section = $("older-rankings"); | |
| const body = $("older-board-body"); | |
| body.innerHTML = ""; | |
| section.hidden = !rankings.length; | |
| $("older-count").textContent = `${rankings.length} stale`; | |
| for (const row of rankings) { | |
| const tr = document.createElement("tr"); | |
| const epochCell = document.createElement("td"); | |
| epochCell.className = "c-rank"; | |
| epochCell.textContent = String(row.lastScoredEpoch); | |
| const hotkeyCell = document.createElement("td"); | |
| appendHotkey(hotkeyCell, row.hotkey); | |
| const qualificationCell = renderScoreCell(row.qualificationScore, null, "c-qualification"); | |
| const fullEvaluationCell = renderScoreCell(row.fullEvaluationScore, null, "c-full-evaluation"); | |
| const finalScoreCell = renderScoreCell(row.finalScore, null, "c-final-score"); | |
| if (row.qualificationScore == null) qualificationCell.textContent = "-"; | |
| if (row.fullEvaluationScore == null) fullEvaluationCell.textContent = "-"; | |
| tr.append(epochCell, hotkeyCell, qualificationCell, fullEvaluationCell, finalScoreCell); | |
| body.appendChild(tr); | |
| } | |
| } | |
| function hasPoints(arr) { | |
| return Array.isArray(arr) && arr.some((x) => x !== null && x !== undefined && !Number.isNaN(x)); | |
| } | |
| function topAccuracySeries(history) { | |
| const epochs = history?.epochs || []; | |
| const correctnessScore = []; | |
| const completionLen = []; | |
| const winners = []; | |
| for (let index = 0; index < epochs.length; index += 1) { | |
| let winner = null; | |
| for (const [hotkey, series] of Object.entries(history?.miners || {})) { | |
| const accuracy = numericScore(series?.correctness_score?.[index]); | |
| if (accuracy == null) continue; | |
| const tokens = numericScore(series?.completion_len?.[index]); | |
| const betterAccuracy = winner == null || accuracy > winner.accuracy; | |
| const betterTie = winner != null && accuracy === winner.accuracy && ( | |
| (tokens ?? Infinity) < (winner.tokens ?? Infinity) | |
| || ((tokens ?? Infinity) === (winner.tokens ?? Infinity) && hotkey < winner.hotkey) | |
| ); | |
| if (betterAccuracy || betterTie) winner = { hotkey, accuracy, tokens }; | |
| } | |
| correctnessScore.push(winner?.accuracy ?? null); | |
| completionLen.push(winner?.tokens ?? null); | |
| winners.push(winner?.hotkey ?? null); | |
| } | |
| return { | |
| epochs, | |
| miner: { correctness_score: correctnessScore, completion_len: completionLen }, | |
| original: history?.original || null, | |
| winners, | |
| }; | |
| } | |
| function renderTrends(view) { | |
| const s = topAccuracySeries(view.history); | |
| let latestIndex = s.miner.correctness_score.length - 1; | |
| while (latestIndex >= 0 && s.miner.correctness_score[latestIndex] == null) latestIndex -= 1; | |
| const completedEpoch = latestIndex >= 0 ? s.epochs[latestIndex] : null; | |
| $("trend-miner").textContent = "Best accuracy each epoch"; | |
| $("trend-epoch").textContent = completedEpoch == null ? "waiting" : `through ${completedEpoch}`; | |
| drawTrend("score", s, "correctness_score"); | |
| drawTrend("len", s, "completion_len"); | |
| } | |
| function drawTrend(which, s, field) { | |
| const canvasId = which === "score" ? "chart-score" : "chart-len"; | |
| const awaitId = which === "score" ? "await-score" : "await-len"; | |
| const minerData = s?.miner?.[field]; | |
| const origData = s?.original?.[field]; | |
| const ok = s && s.epochs.length && (hasPoints(minerData) || hasPoints(origData)); | |
| $(awaitId).hidden = !!ok; | |
| if (!ok) { | |
| destroyChart(which); | |
| return; | |
| } | |
| const ctx = $(canvasId).getContext("2d"); | |
| const datasets = []; | |
| if (hasPoints(minerData)) { | |
| datasets.push(lineDS("Top miner", minerData, ACCENT, css("--accent-ghost"), false, s.winners)); | |
| } | |
| if (hasPoints(origData)) datasets.push(lineDS("Base model", origData, BASE, "transparent", true)); | |
| const cfg = { | |
| type: "line", | |
| data: { labels: s.epochs, datasets }, | |
| options: chartOptions(which === "len" ? "tokens" : "accuracy", which === "score"), | |
| }; | |
| destroyChart(which); | |
| const chart = new Chart(ctx, cfg); | |
| if (which === "score") scoreChart = chart; else lenChart = chart; | |
| } | |
| function lineDS(label, data, color, fill, dashed = false, winnerHotkeys = null) { | |
| return { | |
| label, data, | |
| borderColor: color, | |
| backgroundColor: fill, | |
| borderWidth: 2, | |
| borderDash: dashed ? [5, 4] : [], | |
| pointRadius: 0, | |
| pointHoverRadius: 4, | |
| tension: 0.25, | |
| fill: !dashed, | |
| spanGaps: true, | |
| winnerHotkeys, | |
| }; | |
| } | |
| function chartOptions(yLabel, signed = false) { | |
| const grid = css("--line-soft"); | |
| const tick = css("--faint"); | |
| return { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| interaction: { mode: "index", intersect: false }, | |
| plugins: { | |
| legend: { labels: { color: css("--muted"), boxWidth: 10, boxHeight: 10, usePointStyle: true, font: { size: 11 } } }, | |
| tooltip: { | |
| backgroundColor: "#0b0e12", borderColor: css("--line"), borderWidth: 1, | |
| titleColor: css("--muted"), bodyColor: css("--text"), padding: 10, displayColors: true, | |
| callbacks: { | |
| afterLabel(context) { | |
| const hotkey = context.dataset.winnerHotkeys?.[context.dataIndex]; | |
| if (!hotkey) return ""; | |
| const sk = shortKey(hotkey); | |
| return `miner ${sk.head}${sk.tail}`; | |
| }, | |
| }, | |
| }, | |
| }, | |
| scales: { | |
| x: { title: { display: true, text: "epoch", color: tick, font: { size: 10 } }, grid: { color: grid }, ticks: { color: tick, font: { size: 10 } } }, | |
| y: { | |
| title: { display: true, text: yLabel, color: tick, font: { size: 10 } }, | |
| min: signed ? -1 : undefined, | |
| max: signed ? 1 : undefined, | |
| grid: { color: grid }, | |
| ticks: { color: tick, font: { size: 10 } }, | |
| }, | |
| }, | |
| }; | |
| } | |
| function destroyChart(which) { | |
| if (which === "score" && scoreChart) { scoreChart.destroy(); scoreChart = null; } | |
| if (which === "len" && lenChart) { lenChart.destroy(); lenChart = null; } | |
| } | |
| window.addEventListener("DOMContentLoaded", load); | |