Usage: My usage / Co-op usage mode switch; leaderboard strict descending + cost per request; docs link; drop explainer text

This commit is contained in:
inference-bot committed 2026-10-01 15:47:59 -06:00
1 parent d09bc8a9c6
commit 4be4dc4d03
1 file changed
+66 -26
+66 -26
View File
@@ -325,6 +325,18 @@ PAGE_HTML = """<!DOCTYPE html>
.model-sub { color: var(--muted); font-size: 12px; } .model-sub { color: var(--muted); font-size: 12px; }
.model-spend { font-size: 13px; font-weight: 600; color: var(--ink); text-align: right; } .model-spend { font-size: 13px; font-weight: 600; color: var(--ink); text-align: right; }
/* Usage mode switch */
.usage-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
.usage-header h2 { margin-bottom: 0; }
.mode-switch { display: inline-flex; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.mode-btn {
font-family: inherit; cursor: pointer; border: none; background: #fff; color: var(--muted);
font-size: 12px; font-weight: 600; padding: 6px 12px; border-radius: 0;
}
.mode-btn.active { background: var(--green-deep); color: #fff; }
.docs-link { display: inline-block; margin-top: 14px; color: var(--green-deep); font-size: 13px; font-weight: 600; text-decoration: none; }
.docs-link:hover { text-decoration: underline; }
/* Leaderboard */ /* Leaderboard */
.board-row { .board-row {
display: flex; align-items: flex-start; gap: 14px; display: flex; align-items: flex-start; gap: 14px;
@@ -338,6 +350,8 @@ PAGE_HTML = """<!DOCTYPE html>
} }
.board-row:first-child .board-rank { background: var(--green-deep); color: #fff; } .board-row:first-child .board-rank { background: var(--green-deep); color: #fff; }
.board-main { flex: 1; min-width: 0; } .board-main { flex: 1; min-width: 0; }
.board-line { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.board-cost { color: var(--muted); font-size: 12px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.board-name { font-weight: 600; font-size: 13px; margin-bottom: 5px; } .board-name { font-weight: 600; font-size: 13px; margin-bottom: 5px; }
.board-bar { height: 6px; background: #eef0ea; border-radius: 4px; overflow: hidden; } .board-bar { height: 6px; background: #eef0ea; border-radius: 4px; overflow: hidden; }
.board-fill { height: 100%; background: var(--green-mid); border-radius: 4px; } .board-fill { height: 100%; background: var(--green-mid); border-radius: 4px; }
@@ -468,27 +482,36 @@ PAGE_HTML = """<!DOCTYPE html>
</header> </header>
<div class="card"> <div class="card">
<h2>Usage</h2> <div class="usage-header">
<div class="balance"> <h2>Usage</h2>
<span class="big" id="spend">—</span> <span class="mode-switch">
<span class="lbl" id="of">of your balance used</span> <button class="mode-btn active" id="mode-mine" onclick="setUsageMode('mine')">My usage</button>
<button class="mode-btn" id="mode-coop" onclick="setUsageMode('coop')">Co-op usage</button>
</span>
</div> </div>
<div class="stats">
<div class="stat"><div class="num" id="remaining">—</div><div class="lbl">remaining</div></div>
<div class="stat"><div class="num" id="allowance">—</div><div class="lbl">monthly allowance left</div></div>
<div class="stat"><div class="num" id="credits">—</div><div class="lbl">credits (never expire)</div></div>
<div class="stat"><div class="num" id="tokens">—</div><div class="lbl">tokens used</div></div>
<div class="stat"><div class="num" id="calls">—</div><div class="lbl">requests</div></div>
</div>
<div class="bar"><div class="fill" id="fill" style="width:0%"></div></div>
<div class="meta" id="meta">Shared across chat and API.</div>
<div class="models" id="models"></div>
</div>
<div class="card"> <div id="view-mine">
<h2>Popular in the co-op</h2> <div class="balance">
<p class="hint">Which models members are using most, across the whole co-op. A starting point when choosing one.</p> <span class="big" id="spend">—</span>
<div class="board" id="coop-board"></div> <span class="lbl" id="of">of your balance used</span>
</div>
<div class="stats">
<div class="stat"><div class="num" id="remaining">—</div><div class="lbl">remaining</div></div>
<div class="stat"><div class="num" id="allowance">—</div><div class="lbl">monthly allowance left</div></div>
<div class="stat"><div class="num" id="credits">—</div><div class="lbl">credits (never expire)</div></div>
<div class="stat"><div class="num" id="tokens">—</div><div class="lbl">tokens used</div></div>
<div class="stat"><div class="num" id="calls">—</div><div class="lbl">requests</div></div>
</div>
<div class="bar"><div class="fill" id="fill" style="width:0%"></div></div>
<div class="meta" id="meta">Shared across chat and API.</div>
<div class="models" id="models"></div>
</div>
<div id="view-coop" style="display:none;">
<div class="board" id="coop-board"></div>
</div>
<a class="docs-link" href="https://git.inference.coop/co-op/docs/src/content/docs/models.md" target="_blank">About these models →</a>
</div> </div>
<div class="card"> <div class="card">
@@ -578,7 +601,8 @@ async function load() {
function renderModels(models) { function renderModels(models) {
const el = $('models'); const el = $('models');
if (!models.length) { el.innerHTML = ''; return; } if (!models.length) { el.innerHTML = ''; return; }
el.innerHTML = models.map(m => { const rows = [...models].sort((a, b) => (b.calls || 0) - (a.calls || 0));
el.innerHTML = rows.map(m => {
const sub = (m.calls || 0).toLocaleString() + ' requests · ' + (m.tokens || 0).toLocaleString() + ' tokens'; const sub = (m.calls || 0).toLocaleString() + ' requests · ' + (m.tokens || 0).toLocaleString() + ' tokens';
return '<div class="model-row">' + return '<div class="model-row">' +
'<div><div class="model-name">' + escapeHtml(m.model) + '</div>' + '<div><div class="model-name">' + escapeHtml(m.model) + '</div>' +
@@ -588,22 +612,38 @@ function renderModels(models) {
}).join(''); }).join('');
} }
let usageMode = 'mine'; // 'mine' | 'coop'
function setUsageMode(mode) {
usageMode = mode;
$('mode-mine').className = 'mode-btn' + (mode === 'mine' ? ' active' : '');
$('mode-coop').className = 'mode-btn' + (mode === 'coop' ? ' active' : '');
$('view-mine').style.display = mode === 'mine' ? '' : 'none';
$('view-coop').style.display = mode === 'coop' ? '' : 'none';
}
function renderBoard(models) { function renderBoard(models) {
const el = $('coop-board'); const el = $('coop-board');
if (!models.length) { if (!models.length) {
el.innerHTML = '<div class="empty">Not enough usage data yet — check back soon.</div>'; el.innerHTML = '<div class="empty">Not enough usage data yet — check back soon.</div>';
return; return;
} }
// Rank bar widths are relative to the top model's request count. // Strictly descending by usage (requests); bar widths relative to the top.
const top = Math.max(...models.map(m => m.requests || 0), 1); const rows = [...models].sort((a, b) => (b.requests || 0) - (a.requests || 0));
el.innerHTML = models.map((m, i) => { const top = Math.max(...rows.map(m => m.requests || 0), 1);
const pct = Math.round(((m.requests || 0) / top) * 100); el.innerHTML = rows.map((m, i) => {
const req = m.requests || 0;
const cpr = req > 0 ? (m.spend || 0) / req : 0; // cost per request
const pct = Math.round((req / top) * 100);
return '<div class="board-row">' + return '<div class="board-row">' +
'<div class="board-rank">' + (i + 1) + '</div>' + '<div class="board-rank">' + (i + 1) + '</div>' +
'<div class="board-main">' + '<div class="board-main">' +
'<div class="board-name">' + escapeHtml(m.model) + '</div>' + '<div class="board-line">' +
'<div class="board-name">' + escapeHtml(m.model) + '</div>' +
'<div class="board-cost">' + fmtMoney(cpr) + ' / request</div>' +
'</div>' +
'<div class="board-bar"><div class="board-fill" style="width:' + pct + '%"></div></div>' + '<div class="board-bar"><div class="board-fill" style="width:' + pct + '%"></div></div>' +
'<div class="board-sub">' + (m.requests || 0).toLocaleString() + ' requests by members</div>' + '<div class="board-sub">' + req.toLocaleString() + ' requests by members</div>' +
'</div>' + '</div>' +
'</div>'; '</div>';
}).join(''); }).join('');