import { CONFIG } from "./config.js"; import { costAt, rateText } from "./cost.js"; import * as hf from "./hf.js"; const CAT_COLORS = { code: "#3b82f6", general: "#f97316", cyber: "#10b981", visual: "#f59e0b", chat: "#ec4899" }; const RUN_COLORS = { pro: "#f0643c", flash: "#3b82f6" }; const UNITS = ["second", "minute", "hour", "day", "month", "year"]; const state = { data: null, error: null, authError: null, lastSync: null, tab: "overview", token: null, me: null, isAdmin: false, dirty: false, saveStatus: "", saveFailed: false, jsonDraft: null, jsonError: "", ui: { xMode: "step", yLog: false, smooth: 0, compMode: "count" }, }; let chartSeq = 0; const chartStore = {}; /* ---------- helpers ---------- */ const $ = (sel) => document.querySelector(sel); const esc = (s) => String(s ?? "").replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c])); const fmtInt = (n) => (n == null || isNaN(n) ? "—" : Math.round(n).toLocaleString("en-US")); const fmtMoney = (n) => "$" + fmtInt(n); function fmtNum(v) { if (v == null || isNaN(v)) return "—"; const a = Math.abs(v); if (a >= 1000) return Math.round(v).toLocaleString("en-US"); if (a >= 1) return v.toFixed(2); if (a === 0) return "0"; return v.toPrecision(3); } function fmtBig(n) { if (n == null || isNaN(n)) return "—"; const a = Math.abs(n); if (a >= 1e9) return (n / 1e9).toFixed(2) + "B"; if (a >= 1e6) return (n / 1e6).toFixed(1) + "M"; if (a >= 1e3) return (n / 1e3).toFixed(0) + "k"; return String(Math.round(n)); } function fmtUtc(iso) { const d = new Date(iso); return isNaN(d) ? "—" : d.toISOString().slice(0, 16).replace("T", " ") + " UTC"; } function fmtTime(t) { return isNaN(Date.parse(t)) ? String(t ?? "") : fmtUtc(t); } function fmtDuration(ms) { if (isNaN(ms)) return "—"; const s = Math.max(0, Math.floor(ms / 1000)); const d = Math.floor(s / 86400); const h = String(Math.floor((s % 86400) / 3600)).padStart(2, "0"); const m = String(Math.floor((s % 3600) / 60)).padStart(2, "0"); const sec = String(s % 60).padStart(2, "0"); return `${d}d ${h}:${m}:${sec}`; } const last = (vals) => (vals || []).filter((v) => v != null).slice(-1)[0]; const runName = (k) => state.data?.runs?.find((r) => r.id === k)?.name ?? k; const runColor = (k) => state.data?.runs?.find((r) => r.id === k)?.color ?? RUN_COLORS[k] ?? "#888"; const costNow = () => (state.data ? costAt(state.data.cost, Date.now()) : 0); function metricLast(group, name, runId) { const m = state.data.metrics.find((x) => x.group === group && x.name === name); return m ? last(m.series[runId]) : undefined; } function delta(vals) { const v = (vals || []).filter((x) => x != null); if (v.length < 2) return ""; const a = v[v.length - 1]; const b = v[v.length - 2]; if (v.every((x) => x >= 0 && x <= 1)) { const d = (a - b) * 100; return `${d >= 0 ? "▲" : "▼"} ${Math.abs(d).toFixed(2)} pt`; } const p = b ? ((a - b) / Math.abs(b)) * 100 : 0; return `${p >= 0 ? "▲" : "▼"} ${Math.abs(p).toFixed(1)}%`; } const deltaClass = (vals) => (delta(vals).startsWith("▲") ? "up" : "down"); function smooth(vals, w) { if (w <= 1) return vals; return vals.map((_, i) => { const s = vals.slice(Math.max(0, i - w + 1), i + 1).filter((v) => v != null); return s.length ? s.reduce((x, y) => x + y, 0) / s.length : null; }); } // Hours per training step, used for the "time" x-axis option. function stepHours() { const r = state.data?.runs?.find((x) => x.step > 0 && x.startedAt); if (!r) return 1; const end = r.stoppedAt ? Date.parse(r.stoppedAt) : Date.now(); return (end - Date.parse(r.startedAt)) / 3600000 / r.step; } function xLabel(i) { return state.ui.xMode === "time" ? (i * stepHours()).toFixed(1) + "h" : String(i + 1); } /* ---------- charts ---------- */ function chartSvg(series) { const n = Math.max(0, ...series.map((s) => s.values.length)); const logOn = state.ui.yLog; const ser = series.map((s) => ({ ...s, values: smooth(s.values, state.ui.smooth) })); const ok = (v) => v != null && !isNaN(v) && (!logOn || v > 0); const tv = (v) => (logOn ? Math.log10(v) : v); const flat = ser.flatMap((s) => s.values.filter(ok)); if (n < 2 || !flat.length) return `
no data
`; let lo = tv(Math.min(...flat)); let hi = tv(Math.max(...flat)); if (hi - lo < 1e-9) { lo -= 1; hi += 1; } const pad = (hi - lo) * 0.08; lo -= pad; hi += pad; const W = 600, H = 220, pl = 48, pr = 12, pt = 10, pb = 24; const X = (i) => pl + ((W - pl - pr) * i) / (n - 1); const Y = (v) => pt + (H - pt - pb) * (1 - (tv(v) - lo) / (hi - lo)); const inv = (t) => (logOn ? 10 ** t : t); let grid = ""; for (let k = 0; k <= 4; k++) { const y = pt + (H - pt - pb) * (1 - k / 4); const val = inv(lo + ((hi - lo) * k) / 4); grid += ``; grid += `${fmtNum(val)}`; } const labels = Array.from({ length: n }, (_, i) => xLabel(i)); let xl = ""; for (let k = 0; k <= 4; k++) { const i = Math.round(((n - 1) * k) / 4); xl += `${labels[i]}`; } const paths = ser .map((s) => { let d = ""; let pen = false; s.values.forEach((v, i) => { if (!ok(v)) { pen = false; return; } d += (pen ? "L" : "M") + X(i).toFixed(1) + " " + Y(v).toFixed(1) + " "; pen = true; }); return ``; }) .join(""); const id = "c" + ++chartSeq; chartStore[id] = { ser, labels, n, W, pl, pr }; return `${grid}${xl}${paths}`; } function stackedSvg(cats) { const keys = Object.keys(cats); const n = Math.max(0, ...keys.map((k) => cats[k].length)); if (!n) return `
no data
`; const totals = Array.from({ length: n }, (_, i) => keys.reduce((s, k) => s + (cats[k][i] || 0), 0)); const share = state.ui.compMode === "share"; const W = 600, H = 220, pl = 48, pt = 10, pb = 24; const max = share ? 1 : Math.max(...totals) || 1; const bw = (W - pl) / n; let bars = ""; for (let i = 0; i < n; i++) { let acc = 0; for (const k of keys) { const raw = cats[k][i] || 0; const v = share ? (totals[i] ? raw / totals[i] : 0) : raw; const h = ((H - pt - pb) * v) / max; bars += `step ${i + 1} · ${k}: ${fmtInt(raw)}`; acc += h; } } const ticks = [0, 0.5, 1] .map((f) => { const y = pt + (H - pt - pb) * (1 - f); const label = share ? Math.round(f * 100) + "%" : fmtInt(max * f); return `${label}`; }) .join(""); const xl = [0, Math.floor((n - 1) / 2), n - 1] .map((i) => `${i + 1}`) .join(""); return `${ticks}${bars}${xl}`; } function legendHtml(series, showDelta = false) { return `
${series .map( (s) => `${esc(s.name)} ${fmtNum(last(s.values))}${ showDelta ? ` ${delta(s.values)}` : "" }` ) .join("")}
`; } /* ---------- sections ---------- */ const tabBtn = (id, label) => ``; const segBtn = (k, v, on) => ``; function authHtml() { if (state.isAdmin) { return `ADMIN · @${esc(state.me.name)}`; } if (state.me) { return `@${esc(state.me.name)} · read-only`; } return ``; } function saveHtml() { if (!state.saveStatus) return ""; return ( `${esc(state.saveStatus)}` + (state.saveFailed ? `` : "") ); } function runCard(r) { const running = r.status === "running" && !r.stoppedAt; const start = Date.parse(r.startedAt); const stop = r.stoppedAt ? Date.parse(r.stoppedAt) : ""; const stat = (label, value) => `
${label}${value}
`; return `
${esc(r.name)} ${running ? "running" : "stopped"} step ${fmtInt(r.step)} ${fmtDuration((stop || Date.now()) - start)}
${stat("avg@n", fmtNum(metricLast("dynsam", "avg@n", r.id)))} ${stat("cost so far", fmtMoney(r.costSoFar))} ${stat("tokens · step", fmtBig(r.tokensAtStep))} ${stat("tokens · total", fmtBig(r.tokensTotal))} ${stat("samples trained", fmtBig(r.samplesTrained))} ${stat("train batch × rollouts", `${fmtInt(r.trainBatch)} × ${r.rollouts ?? "—"}`)}
`; } function noticesHtml() { const items = [...state.data.notices].sort((a, b) => Date.parse(b.time) - Date.parse(a.time)); const form = state.isAdmin ? `
${saveHtml()}
` : ""; const list = items.length ? items .map( (n) => `
${fmtUtc(n.time)} ${esc(n.text)} ${state.isAdmin ? `` : ""}
` ) .join("") : `
No notices.
`; return `
noticesUTC
${form} ${list}
`; } function readCostForm() { const raw = $("#cost-correct").value.trim(); const correct = raw === "" ? Math.round(costNow()) : Number(raw); const inc = Number($("#cost-inc").value); const every = Number($("#cost-every").value); const unit = $("#cost-unit").value; const ok = isFinite(correct) && isFinite(inc) && inc >= 0 && isFinite(every) && every > 0; return { ok, correct, inc, every, unit }; } function updateCostPreview() { const el = $("#cost-preview"); if (!el) return; const f = readCostForm(); el.textContent = f.ok ? `Preview: shows ${fmtMoney(f.correct)} now, then ${rateText({ increment: f.inc, every: f.every, unit: f.unit })}` : "Enter valid numbers."; } function adminCostHtml() { const c = state.data.cost; return `
admin · costsaved to the dataset repo
${saveHtml()}
`; } function benchHtml() { const bs = state.data.benchmarks; if (!bs.length) return ""; const cards = bs .map((b) => { const series = Object.keys(b.series).map((k) => ({ name: runName(k), color: runColor(k), values: b.series[k] })); return `
${esc(b.name)}${esc(b.subtitle || "")}
${legendHtml(series)} ${chartSvg(series)}
`; }) .join(""); return `

benchmarks

${cards}
`; } function batchHtml() { const bc = state.data.batchComposition || {}; const cats = bc.categories || {}; const keys = Object.keys(cats); if (!keys.length) return ""; const legend = keys .map((c) => `${esc(c)}`) .join(""); return `

batch composition

what each step's samples are made of · by data source
${segBtn("compMode", "count", state.ui.compMode === "count")}${segBtn("compMode", "share", state.ui.compMode === "share")}
${legend}
${stackedSvg(cats)}
`; } function sourcesHtml() { const src = state.data.sources; if (!src.length) return ""; const latest = Math.max(...src.map((s) => s.step)); const rows = src .filter((s) => s.step === latest) .sort((a, b) => b.accepted - a.accepted) .map((r) => { const pct = r.target ? Math.min(100, (r.accepted / r.target) * 100) : 0; return ` ${esc(runName(r.run))} ${esc(r.name)} ● ${esc(r.category)} ${fmtInt(r.accepted)} / ${fmtInt(r.target)}
${fmtInt(r.remaining)} ${r.judged == null ? "—" : fmtInt(r.judged)} ${fmtInt(r.inFlight)} `; }) .join(""); return `

sources

latest step · ${latest}
${rows}
runsourcecategoryaccepted / targetremainingjudgedin flight
`; } function feedHtml() { const f = state.data.feed; const items = f .map((x) => `
${esc(fmtTime(x.time))} ${esc(x.message)}
`) .join(""); return `

feed

feedUTC
${items || `
No events.
`}
`; } function overviewHtml() { return ` ${state.isAdmin ? adminCostHtml() : ""} ${noticesHtml()}
${state.data.runs.map(runCard).join("")}
${benchHtml()} ${batchHtml()} ${sourcesHtml()} ${feedHtml()}`; } function metricCard(m) { const series = Object.keys(m.series).map((k) => ({ name: runName(k), color: runColor(k), values: m.series[k] })); return `
${esc(m.group)}/${esc(m.name)}
${legendHtml(series, true)} ${chartSvg(series)}
`; } function metricGridHtml() { const ms = state.data.metrics; return ms.length ? ms.map(metricCard).join("") : `
No metrics.
`; } function metricsHtml() { const ui = state.ui; return `
${segBtn("xMode", "step", ui.xMode === "step")}${segBtn("xMode", "time", ui.xMode === "time")}
${segBtn("yLog", "linear", !ui.yLog)}${segBtn("yLog", "log", ui.yLog)}
${metricGridHtml()}
`; } function renderMd(src) { return esc(src || "") .split(/\n\s*\n/) .map((block) => { if (/^#\s/.test(block)) return `

${block.replace(/^#\s/, "")}

`; if (/^- /m.test(block)) { const items = block.split("\n").map((l) => `
  • ${l.replace(/^- /, "")}
  • `).join(""); return `
      ${items}
    `; } return `

    ${block.replace(/\n/g, "
    ")}

    `; }) .join(""); } function aboutHtml() { const admin = state.isAdmin ? `
    edit about# heading · - bullets · blank line = new paragraph
    ${saveHtml()}
    ` : ""; return `
    ${renderMd(state.data.about)}
    ${admin}`; } function jsonHtml() { const text = state.jsonDraft ?? JSON.stringify(state.data, null, 2); return `
    Edit data (JSON)validated before saving
    ${state.jsonError ? "⚠ " + esc(state.jsonError) : "valid JSON"} ${state.dirty ? `unsaved changes` : ""} ${saveHtml()}
    `; } function mainHtml() { if (state.tab === "metrics") return metricsHtml(); if (state.tab === "about") return aboutHtml(); if (state.tab === "json" && state.isAdmin) return jsonHtml(); return overviewHtml(); } function footerHtml() { const f = state.data.footer || {}; const synced = state.lastSync ? ` · synced ${state.lastSync.toLocaleTimeString("en-GB", { hour12: false })}` : ""; return `
    @${esc(CONFIG.OWNER_HF_USERNAME)} · streaming since ${fmtUtc(f.streamingSince)} · ${esc(f.tagline || "")}${synced}
    `; } /* ---------- render & ticks ---------- */ function render() { const d = state.data; const banner = state.error || state.authError; $("#app").innerHTML = `
    RL training
    total cost ${d ? fmtMoney(costNow()) : "—"} ${d ? rateText(d.cost) : ""}
    ${CONFIG.DISPLAY_TIMEZONES .map((z) => `
    ${esc(z.label)}
    `) .join("")}
    ${authHtml()}
    ${banner && d ? `` : ""} ${banner && !d ? `` : ""}
    ${d ? mainHtml() : `
    Loading…
    `}
    ${d ? footerHtml() : ""} `; tickTime(); tickCost(); updateCostPreview(); } function tickCost() { if (!state.data) return; const txt = fmtMoney(costNow()); document.querySelectorAll("[data-cost]").forEach((el) => (el.textContent = txt)); } function tickTime() { const now = new Date(); document.querySelectorAll("[data-clock]").forEach((el) => { el.textContent = new Intl.DateTimeFormat("en-GB", { timeZone: el.dataset.clock, hour: "2-digit", minute: "2-digit", second: "2-digit", hourCycle: "h23", }).format(now); }); document.querySelectorAll(".elapsed").forEach((el) => { const start = Number(el.dataset.start); const end = el.dataset.stop ? Number(el.dataset.stop) : Date.now(); el.textContent = fmtDuration(end - start); }); } /* ---------- data flow ---------- */ function normalize(d) { const cost = { base: 0, baseTime: new Date().toISOString(), increment: 0, every: 1, unit: "hour", ...(d.cost || {}), }; return { notices: [], runs: [], metrics: [], benchmarks: [], sources: [], feed: [], about: "", footer: {}, ...d, cost, batchComposition: d.batchComposition || { categories: {} }, }; } async function sync(force = false) { if (state.dirty && !force) return; try { state.data = normalize(await hf.readData()); state.error = null; state.lastSync = new Date(); } catch (e) { state.error = e.message; } render(); } async function save() { if (!state.token) return; state.saveStatus = "Saving..."; state.saveFailed = false; render(); try { await hf.writeData(state.token, state.data); state.dirty = false; state.jsonDraft = null; state.jsonError = ""; state.saveStatus = "Saved"; await sync(true); } catch (e) { state.saveStatus = "Failed: " + e.message; state.saveFailed = true; render(); } } async function refreshIdentity() { state.me = await hf.whoami(state.token).catch(() => null); if (!state.me) { hf.logout(); state.token = null; } state.isAdmin = !!state.me && state.me.name?.toLowerCase() === CONFIG.OWNER_HF_USERNAME.toLowerCase(); } async function init() { try { state.token = (await hf.handleRedirect()) || sessionStorage.getItem("hf_token"); } catch (e) { state.authError = e.message; } if (state.token) await refreshIdentity(); await sync(true); setInterval(() => sync(false), CONFIG.REFRESH_MS); setInterval(tickCost, 100); setInterval(tickTime, 1000); } /* ---------- events ---------- */ document.addEventListener("click", async (e) => { const el = e.target.closest("[data-action]"); if (!el) return; switch (el.dataset.action) { case "tab": state.tab = el.dataset.tab; render(); break; case "login": hf.login(); break; case "logout": hf.logout(); state.token = null; state.me = null; state.isAdmin = false; state.dirty = false; render(); break; case "theme": document.body.classList.toggle("light"); break; case "ui": { const k = el.dataset.k; const v = el.dataset.v; state.ui[k] = k === "yLog" ? v === "log" : v; render(); break; } case "post-notice": { const text = $("#notice-text").value.trim(); if (!text) break; state.data.notices.push({ id: "n" + Date.now(), time: new Date().toISOString(), text }); save(); break; } case "del-notice": if (confirm("Delete this notice?")) { state.data.notices = state.data.notices.filter((n) => n.id !== el.dataset.id); save(); } break; case "save-cost": { const f = readCostForm(); if (!f.ok) { state.saveStatus = "Invalid cost values"; state.saveFailed = true; render(); break; } state.data.cost = { base: f.correct, baseTime: new Date().toISOString(), increment: f.inc, every: f.every, unit: f.unit, }; save(); break; } case "save-about": state.data.about = $("#about-text").value; save(); break; case "json-save": { try { const parsed = JSON.parse(state.jsonDraft ?? JSON.stringify(state.data)); state.data = normalize(parsed); state.jsonError = ""; save(); } catch (err) { state.jsonError = err.message; render(); } break; } case "json-discard": state.jsonDraft = null; state.jsonError = ""; state.dirty = false; sync(true); break; case "save": save(); break; } }); function handleField(e) { const t = e.target; if (t.closest?.("[data-admin]")) state.dirty = true; if (t.id === "json-edit") { state.jsonDraft = t.value; try { JSON.parse(t.value); state.jsonError = ""; } catch (err) { state.jsonError = err.message; } const s = $("#json-status"); if (s) { s.textContent = state.jsonError ? "⚠ " + state.jsonError : "valid JSON"; s.className = state.jsonError ? "err" : "muted"; } } if (t.id?.startsWith("cost-")) updateCostPreview(); if (t.id === "smooth") { state.ui.smooth = Number(t.value); $("#smooth-val").textContent = state.ui.smooth > 1 ? state.ui.smooth + " pts" : "off"; $("#metric-grid").innerHTML = metricGridHtml(); } } document.addEventListener("input", handleField); document.addEventListener("change", handleField); document.addEventListener("pointermove", (e) => { const tip = $("#tip"); const svg = e.target.closest?.("svg[data-chart]"); const c = svg && chartStore[svg.dataset.chart]; if (!c) { tip.hidden = true; return; } const r = svg.getBoundingClientRect(); const x = ((e.clientX - r.left) / r.width) * c.W; const frac = (x - c.pl) / (c.W - c.pl - c.pr); const i = Math.max(0, Math.min(c.n - 1, Math.round(frac * (c.n - 1)))); tip.innerHTML = `
    step ${esc(c.labels[i])}
    ` + c.ser .map((s) => `
    ${esc(s.name)}: ${fmtNum(s.values[i])}
    `) .join(""); tip.hidden = false; tip.style.left = e.clientX + 14 + "px"; tip.style.top = e.clientY + 14 + "px"; }); init();