hyperdex-trainer / web /templates /explore.html
GGUFGuy's picture
Duplicate from GGUFGuy/hyperdex-trainer
2a49d9a
Raw History Blame Contribute Delete
6.01 kB
{% extends "base.html" %}
{% set nav = 'explore' %}
{% block title %}Explore — NanoDex{% endblock %}
{% block body %}
<div class="sect-h" style="margin-bottom:22px">
<div>
<span class="eyebrow">The shelf</span>
<h1 style="font-size:28px;margin-top:5px">Every model trained here</h1>
<p class="sub" style="margin-top:5px">Same tokenizer, same corpus, same
context length for everyone — so the losses below compare directly. Open
any model and generate text from it.</p>
</div>
</div>
<div class="grid g4" style="margin-bottom:24px">
<div class="stat"><span>Models</span><b id="s-done">—</b><small>finished runs</small></div>
<div class="stat"><span>Training now</span><b id="s-run">—</b><small>on workers</small></div>
<div class="stat"><span>Trainers</span><b id="s-users">—</b><small>people</small></div>
<div class="stat"><span>Tokens burned</span><b id="s-tok">—</b><small>all time</small></div>
</div>
<section id="liveSec" style="margin-bottom:32px;display:none">
<div class="sect-h"><h2>Being born right now</h2></div>
<div class="runs" id="live"></div>
</section>
<section style="margin-bottom:32px">
<div class="sect-h" style="margin-bottom:12px">
<h2>Best loss by size</h2>
<span class="sub">lower is better</span>
</div>
<div id="board" class="grid g2"></div>
</section>
<section style="margin-bottom:32px">
<div class="sect-h" style="margin-bottom:12px"><h2>All models</h2></div>
<div style="display:flex;gap:7px;flex-wrap:wrap;margin-bottom:8px" id="tierFilter">
<button class="btn sm primary" data-tier="">All sizes</button>
{% for t in tiers %}
<button class="btn sm" data-tier="{{ t.key }}">{{ t.label }}</button>
{% endfor %}
</div>
<div style="display:flex;gap:7px;flex-wrap:wrap;margin-bottom:18px" id="sortFilter">
<button class="btn sm primary" data-sort="recent">Newest</button>
<button class="btn sm" data-sort="loss">Lowest loss</button>
<button class="btn sm" data-sort="tokens">Most tokens</button>
<button class="btn sm" data-sort="params">Largest</button>
</div>
<div id="models"></div>
</section>
<section>
<div class="sect-h" style="margin-bottom:12px"><h2>Trainers</h2></div>
<div id="trainers" class="grid g3"></div>
</section>
{% endblock %}
{% block scripts %}
<script>
let tier = '', sort = 'recent';
function card(m){
return `<a class="run" href="/models/${m.id}">
<div class="run-top">
<h4>${NX.esc(m.name)}</h4>
${NX.chip(m.status)}
</div>
<div class="run-meta">
<span class="mono">${NX.esc(m.tier_label)}</span>
<span>${Number(m.n_params).toLocaleString()} params</span>
<span><b>${NX.fmtTokens(m.tokens_seen)}</b> tokens</span>
${m.best_loss ? `<span>loss <b>${m.best_loss.toFixed(4)}</b></span>` : ''}
<span>@${NX.esc(m.username)}</span>
<span class="muted">${NX.ago(m.finished_at || m.created_at)}</span>
</div>
</a>`;
}
function bind(sel, key, fn){
document.querySelectorAll(sel + ' .btn').forEach(b =>
b.addEventListener('click', () => {
document.querySelectorAll(sel + ' .btn').forEach(x => x.classList.remove('primary'));
b.classList.add('primary');
fn(b.dataset[key]);
loadModels();
}));
}
bind('#tierFilter', 'tier', v => tier = v);
bind('#sortFilter', 'sort', v => sort = v);
async function loadModels(){
const q = await NX.get(`/api/explore?tier=${tier}&sort=${sort}&limit=60`);
document.getElementById('s-done').textContent = q.stats.done;
document.getElementById('s-run').textContent = q.stats.running;
document.getElementById('s-users').textContent = q.stats.users;
document.getElementById('s-tok').textContent = NX.fmtTokens(q.stats.tokens);
const liveSec = document.getElementById('liveSec');
liveSec.style.display = q.active.length ? 'block' : 'none';
document.getElementById('live').innerHTML = q.active.map(NX.runRow).join('');
document.getElementById('models').innerHTML = q.models.length
? `<div class="runs">${q.models.map(card).join('')}</div>`
: `<div class="empty">No finished models${tier ? ' at this size' : ''} yet.</div>`;
}
async function loadBoard(){
const {tiers} = await NX.get('/api/leaderboard');
const el = document.getElementById('board');
if(!tiers.length){
el.innerHTML = `<div class="empty" style="grid-column:1/-1">
Nothing finished yet — the first model trained here takes every record.</div>`;
return;
}
el.innerHTML = tiers.map(t => `<div class="card">
<div class="eyebrow" style="margin-bottom:10px">${NX.esc(t.label)}</div>
${t.models.map((m,i) => `<div class="kv">
<span><b class="mono" style="color:var(--accent)">#${i+1}</b>
<a href="/models/${m.id}" style="color:var(--fg)">${NX.esc(m.name)}</a>
<span class="muted">@${NX.esc(m.username)}</span></span>
<b>${m.best_loss.toFixed(4)}</b></div>`).join('')}
</div>`).join('');
}
async function loadTrainers(){
const {trainers} = await NX.get('/api/trainers');
document.getElementById('trainers').innerHTML = trainers.length
? trainers.map(t => `<a class="card" href="/u/${encodeURIComponent(t.username)}"
style="display:flex;gap:12px;align-items:center">
${t.avatar_url
? `<img class="avatar" style="width:40px;height:40px" src="${NX.esc(t.avatar_url)}" alt="">`
: `<span class="avatar" style="width:40px;height:40px"></span>`}
<div style="min-width:0">
<div style="font-weight:620">${NX.esc(t.display_name || t.username)}</div>
<div class="sub" style="font-size:12.5px">
${t.done} model${t.done === 1 ? '' : 's'} ·
${NX.fmtTokens(t.tokens)} tokens${t.best_loss
? ' · best ' + t.best_loss.toFixed(3) : ''}</div>
</div></a>`).join('')
: `<div class="empty" style="grid-column:1/-1">No trainers yet.</div>`;
}
NX.poll(async () => { await loadModels(); await loadBoard(); await loadTrainers(); }, 8000);
</script>
{% endblock %}