File size: 6,011 Bytes
2a49d9a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | {% 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 %}
|