This commit is contained in:
Dobromir Popov
2026-07-08 18:24:45 +02:00
parent e06969fcb5
commit 29db25108f
3 changed files with 112 additions and 38 deletions

View File

@@ -279,6 +279,41 @@ const tps = v => (v === null || v === undefined) ? "?" : (Math.round(v * 10) / 1
const copies = v => (v === null || v === undefined) ? "?" : Number(v).toFixed(2);
const short = (s, n=14) => { s = String(s); return s.length > n ? s.slice(0, 6) + "…" + s.slice(-5) : s; };
function modelAliasKey(value) {
if (!value) return "";
const text = String(value).trim();
if (!text) return "";
const shortName = text.includes("/") ? text.split("/").pop() : text;
return shortName.toLowerCase();
}
function buildModelAliasMap(map) {
const byAlias = new Map();
const register = (display, ...names) => {
if (!display) return;
for (const name of names) {
const key = modelAliasKey(name);
if (key) byAlias.set(key, display);
}
};
for (const entry of (map && map.recommended_models) || []) {
register(entry.id, entry.id, entry.hf_repo, ...(entry.aliases || []));
}
for (const entry of availableModels || []) {
register(entry.name || entry.id, entry.id, entry.name, ...(entry.aliases || []));
}
return byAlias;
}
function resolveModelGroup(node, aliasMap) {
for (const candidate of [node.hf_repo, node.model]) {
if (!candidate) continue;
const hit = aliasMap.get(modelAliasKey(candidate));
if (hit) return hit;
}
return node.hf_repo || node.model || "?";
}
async function fetchJson(path) {
try {
const headers = {};
@@ -315,15 +350,20 @@ function renderNodes(map) {
if (!nodes.length) {
$("nodes").innerHTML = '<div class="empty">no nodes registered</div>'; return;
}
const aliasMap = buildModelAliasMap(map);
const byModel = {};
for (const n of nodes) {
const key = n.model || n.hf_repo || "?";
const key = resolveModelGroup(n, aliasMap);
(byModel[key] = byModel[key] || []).push(n);
}
const modelNames = Object.keys(byModel).sort((a, b) => a.localeCompare(b));
let html = "";
for (const [model, group] of Object.entries(byModel)) {
const supply = group.find(n => n.model_supply && n.model_supply.served_model_copies !== undefined);
const served = supply && supply.model_supply && supply.model_supply.served_model_copies;
for (const model of modelNames) {
const group = byModel[model];
const servedValues = group
.map(n => n.model_supply && n.model_supply.served_model_copies)
.filter(v => v !== null && v !== undefined);
const served = servedValues.length ? Math.max(...servedValues) : undefined;
html += `<div><b>${esc(model)}</b> <span class="dim">(${group.length} node${group.length===1?"":"s"} · ${esc(copies(served))} served)</span></div>`;
html += table(["node", "shard", "tps (1h)", "queue", "served"], group.map(n => {
const modelStats = (n.throughput && (n.throughput[n.hf_repo] || n.throughput[n.model])) || {};
@@ -1552,6 +1592,19 @@ async function refresh() {
renderChatHistory();
$("refreshed").textContent = "refreshed " + new Date().toLocaleTimeString();
}
const REFRESH_MS = 10000;
function selectionActive() {
const sel = window.getSelection();
return sel && !sel.isCollapsed && sel.toString().length > 0;
}
async function refreshIfIdle() {
if (selectionActive()) return;
await refresh();
}
refresh();
initChatSessions();
bindChatPromptShortcuts();
@@ -1559,8 +1612,12 @@ renderAccountPanel();
renderChatModels();
renderChatHistory();
renderChatAuthHint();
setInterval(refresh, 4000);
setInterval(() => { if (sessionToken || isLoggedIn) renderAccountPanel(); }, 8000);
setInterval(refreshIfIdle, REFRESH_MS);
setInterval(() => {
if (!sessionToken && !isLoggedIn) return;
if (selectionActive()) return;
renderAccountPanel();
}, REFRESH_MS);
</script>
</body>
</html>