Usage: My usage / Co-op usage mode switch; leaderboard strict descending + cost per request; docs link; drop explainer text
This commit is contained in:
1 parent
d09bc8a9c6
commit
4be4dc4d03
1 file changed
+66
-26
+66
-26
@@ -325,6 +325,18 @@ PAGE_HTML = """<!DOCTYPE html>
|
||||
.model-sub { color: var(--muted); font-size: 12px; }
|
||||
.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 */
|
||||
.board-row {
|
||||
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-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-bar { height: 6px; background: #eef0ea; border-radius: 4px; overflow: hidden; }
|
||||
.board-fill { height: 100%; background: var(--green-mid); border-radius: 4px; }
|
||||
@@ -468,27 +482,36 @@ PAGE_HTML = """<!DOCTYPE html>
|
||||
</header>
|
||||
|
||||
<div class="card">
|
||||
<h2>Usage</h2>
|
||||
<div class="balance">
|
||||
<span class="big" id="spend">—</span>
|
||||
<span class="lbl" id="of">of your balance used</span>
|
||||
<div class="usage-header">
|
||||
<h2>Usage</h2>
|
||||
<span class="mode-switch">
|
||||
<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 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">
|
||||
<h2>Popular in the co-op</h2>
|
||||
<p class="hint">Which models members are using most, across the whole co-op. A starting point when choosing one.</p>
|
||||
<div class="board" id="coop-board"></div>
|
||||
<div id="view-mine">
|
||||
<div class="balance">
|
||||
<span class="big" id="spend">—</span>
|
||||
<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 class="card">
|
||||
@@ -578,7 +601,8 @@ async function load() {
|
||||
function renderModels(models) {
|
||||
const el = $('models');
|
||||
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';
|
||||
return '<div class="model-row">' +
|
||||
'<div><div class="model-name">' + escapeHtml(m.model) + '</div>' +
|
||||
@@ -588,22 +612,38 @@ function renderModels(models) {
|
||||
}).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) {
|
||||
const el = $('coop-board');
|
||||
if (!models.length) {
|
||||
el.innerHTML = '<div class="empty">Not enough usage data yet — check back soon.</div>';
|
||||
return;
|
||||
}
|
||||
// Rank bar widths are relative to the top model's request count.
|
||||
const top = Math.max(...models.map(m => m.requests || 0), 1);
|
||||
el.innerHTML = models.map((m, i) => {
|
||||
const pct = Math.round(((m.requests || 0) / top) * 100);
|
||||
// Strictly descending by usage (requests); bar widths relative to the top.
|
||||
const rows = [...models].sort((a, b) => (b.requests || 0) - (a.requests || 0));
|
||||
const top = Math.max(...rows.map(m => m.requests || 0), 1);
|
||||
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">' +
|
||||
'<div class="board-rank">' + (i + 1) + '</div>' +
|
||||
'<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-sub">' + (m.requests || 0).toLocaleString() + ' requests by members</div>' +
|
||||
'<div class="board-sub">' + req.toLocaleString() + ' requests by members</div>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}).join('');
|
||||
|
||||
Reference in new issue
Block a user