node and account names
This commit is contained in:
@@ -280,6 +280,41 @@ const copies = v => (v === null || v === undefined) ? "?" : Number(v).toFixed(2)
|
||||
const hp = v => (v === null || v === undefined) ? "?HP" : `${copies(v)}HP`;
|
||||
const short = (s, n=14) => { s = String(s); return s.length > n ? s.slice(0, 6) + "…" + s.slice(-5) : s; };
|
||||
|
||||
function accountDisplayName(account) {
|
||||
if (!account) return "?";
|
||||
const nickname = (account.nickname || "").trim();
|
||||
if (nickname) return nickname;
|
||||
return account.email || account.wallet || account.account_id || "?";
|
||||
}
|
||||
|
||||
function buildNodeNameMap(map) {
|
||||
const byId = new Map();
|
||||
for (const node of (map && map.nodes) || []) {
|
||||
const name = (node.friendly_name || "").trim();
|
||||
if (node.node_id && name) byId.set(node.node_id, name);
|
||||
}
|
||||
return byId;
|
||||
}
|
||||
|
||||
function nodeDisplayName(nodeOrId, nameMap) {
|
||||
if (nodeOrId && typeof nodeOrId === "object") {
|
||||
const friendly = (nodeOrId.friendly_name || "").trim();
|
||||
if (friendly) return friendly;
|
||||
return short(nodeOrId.node_id || "?");
|
||||
}
|
||||
const friendly = nameMap && nameMap.get(nodeOrId);
|
||||
return friendly || short(nodeOrId || "?");
|
||||
}
|
||||
|
||||
function nodeDisplayCell(node) {
|
||||
const label = esc(nodeDisplayName(node));
|
||||
const friendly = (node.friendly_name || "").trim();
|
||||
if (friendly && node.node_id) {
|
||||
return `<span title="${esc(node.node_id)}">${label}</span>`;
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
function modelAliasKey(value) {
|
||||
if (!value) return "";
|
||||
const text = String(value).trim();
|
||||
@@ -398,7 +433,7 @@ function renderNodes(map) {
|
||||
html += table(["node", "shard", "tps (1h)", "queue", "served"], group.map(n => {
|
||||
const modelStats = (n.throughput && (n.throughput[n.hf_repo] || n.throughput[n.model])) || {};
|
||||
return [
|
||||
esc(short(n.node_id || "?")),
|
||||
nodeDisplayCell(n),
|
||||
esc(`${n.shard_start ?? "?"}-${n.shard_end ?? "?"}`),
|
||||
`<span class="num">${esc(tps(modelStats.tokens_per_sec_last_hour))}</span>`,
|
||||
esc(String((n.stats && n.stats.queue_depth) ?? 0)),
|
||||
@@ -475,11 +510,12 @@ function renderStats(stats) {
|
||||
|
||||
function renderThroughputHtml(stats) {
|
||||
const nodes = (stats && stats.nodes) || {};
|
||||
const nameMap = buildNodeNameMap(lastNetworkMap);
|
||||
const rows = [];
|
||||
for (const [nodeId, nodeStats] of Object.entries(nodes)) {
|
||||
for (const [model, s] of Object.entries((nodeStats && nodeStats.models) || {})) {
|
||||
rows.push([
|
||||
esc(short(nodeId)),
|
||||
esc(nodeDisplayName(nodeId, nameMap)),
|
||||
esc(short(model, 24)),
|
||||
`<span class="num">${esc(tps(s.tokens_per_sec_last_hour))}</span>`,
|
||||
`<span class="num">${esc(String(s.sample_count_last_hour ?? 0))}</span>`,
|
||||
@@ -612,6 +648,7 @@ function renderRouting(routing) {
|
||||
return;
|
||||
}
|
||||
const cfg = (routing && routing.config) || {};
|
||||
const nameMap = buildNodeNameMap(lastNetworkMap);
|
||||
let html = `<div class="dim" style="margin-bottom:6px">` +
|
||||
`explore share: <b>${esc(String(cfg.explore_share ?? "?"))}</b> · ` +
|
||||
`traffic ∝ tps^<b>${esc(String(cfg.weight_alpha ?? "?"))}</b> · ` +
|
||||
@@ -621,7 +658,7 @@ function renderRouting(routing) {
|
||||
html += `<div style="margin-top:6px"><b>${esc(model)}</b> ` +
|
||||
`<span class="dim">(epoch ${esc(String(info.epoch ?? 0))} · ${routes.length} route${routes.length === 1 ? "" : "s"})</span></div>`;
|
||||
html += table(["route", "tps", "coeff", "share", "samples", "status"], routes.map(r => {
|
||||
const hops = (r.hops || []).map(h => `${short(h.node_id, 12)}[${h.shard}]`).join(" → ");
|
||||
const hops = (r.hops || []).map(h => `${nodeDisplayName(h.node_id, nameMap)}[${h.shard}]`).join(" → ");
|
||||
const statusCls = r.status === "proven" ? "ok" : r.status === "stale" ? "warn" : "dim";
|
||||
const coeff = (r.coefficient === null || r.coefficient === undefined)
|
||||
? "—" : Number(r.coefficient).toFixed(2) + "×";
|
||||
@@ -1259,7 +1296,8 @@ function renderAuthForms(errorMsg) {
|
||||
`<a class="${authTab === name ? "active" : ""}" onclick="switchAuthTab('${name}')">${label}</a>`;
|
||||
const identityFields =
|
||||
'<input id="auth-email" type="email" placeholder="email (or leave blank)">' +
|
||||
'<input id="auth-wallet" type="text" placeholder="wallet address (or leave blank)">';
|
||||
'<input id="auth-wallet" type="text" placeholder="wallet address (or leave blank)">' +
|
||||
'<input id="auth-nickname" type="text" placeholder="nickname (optional)">';
|
||||
const form = authTab === "login"
|
||||
? '<input id="auth-identifier" type="text" placeholder="email or wallet address">' +
|
||||
'<input id="auth-password" type="password" placeholder="password">' +
|
||||
@@ -1281,9 +1319,10 @@ function switchAuthTab(name) { authTab = name; renderAuthForms(); }
|
||||
async function doRegister() {
|
||||
const email = $("auth-email").value.trim();
|
||||
const wallet = $("auth-wallet").value.trim();
|
||||
const nickname = $("auth-nickname").value.trim();
|
||||
const password = $("auth-password").value;
|
||||
const r = await apiCall("/v1/auth/register", "POST",
|
||||
{ email: email || null, wallet: wallet || null, password });
|
||||
{ email: email || null, wallet: wallet || null, nickname: nickname || null, password });
|
||||
if (!r.ok) { renderAuthForms(r.data.error || "registration failed"); return; }
|
||||
setSession(r.data.session_token);
|
||||
await renderAccountPanel();
|
||||
@@ -1382,6 +1421,13 @@ function renderChatAuthHint() {
|
||||
}
|
||||
}
|
||||
|
||||
async function saveNickname() {
|
||||
const nickname = $("account-nickname").value.trim();
|
||||
const r = await apiCall("/v1/account/profile", "POST", { nickname: nickname || null });
|
||||
if (!r.ok) alert(r.data.error || "nickname update failed");
|
||||
else await renderAccountPanel();
|
||||
}
|
||||
|
||||
async function renderAccountPanel() {
|
||||
const r = await apiCall("/v1/account");
|
||||
if (r.status === 404) { // accounts disabled on this tracker
|
||||
@@ -1397,10 +1443,14 @@ async function renderAccountPanel() {
|
||||
const { account, api_keys, balances, total_balance, usage, topup_amount } = r.data;
|
||||
accountApiKeys = Array.isArray(api_keys) ? api_keys.slice() : [];
|
||||
accountUsageRecords = (usage && (usage.records || usage.recent)) || [];
|
||||
const who = account.email || account.wallet || account.account_id;
|
||||
const who = accountDisplayName(account);
|
||||
let html =
|
||||
`<div><b>${esc(who)}</b> <span class="pill">${esc(account.role)}</span> ` +
|
||||
`<button class="small" style="float:right" onclick="doLogout()">log out</button></div>` +
|
||||
`<div style="margin:6px 0;display:flex;gap:8px;align-items:center;flex-wrap:wrap">` +
|
||||
`<label class="dim" style="margin:0">nickname</label>` +
|
||||
`<input id="account-nickname" type="text" value="${esc(account.nickname || "")}" placeholder="display name" style="min-width:180px">` +
|
||||
`<button class="small" type="button" onclick="saveNickname()">save</button></div>` +
|
||||
`<div style="margin:6px 0">balance: <b class="${total_balance > 0 ? "ok" : "bad"}">` +
|
||||
`${usdt(total_balance)}</b> USDT · requests: <b>${usage.requests}</b> · ` +
|
||||
`tokens: <b>${usage.total_tokens}</b> · spent: <b>${usdt(usage.total_cost)}</b> USDT</div>`;
|
||||
@@ -1617,7 +1667,7 @@ async function renderAdminPanel() {
|
||||
const rows = (r.data.accounts || []).map(a => {
|
||||
const balance = Object.values(a.balances || {}).reduce((x, y) => x + y, 0);
|
||||
return [
|
||||
esc(short(a.email || a.wallet || a.account_id, 24)),
|
||||
esc(short(accountDisplayName(a), 24)),
|
||||
`<span class="pill">${esc(a.role)}</span>`,
|
||||
String((a.api_keys || []).length),
|
||||
`<span class="num ${balance > 0 ? "ok" : "bad"}">${usdt(balance)}</span>`,
|
||||
|
||||
Reference in New Issue
Block a user