fix: make model placement controls observable

This commit is contained in:
Dobromir Popov
2026-07-14 16:00:37 +02:00
parent 348b003d6e
commit 35af1e21de
2 changed files with 26 additions and 7 deletions

View File

@@ -295,7 +295,7 @@
<section data-tab="billing" data-admin-only><h2>Node pending payouts</h2><div id="pending" class="empty">admin login required</div></section>
<section data-tab="billing" data-admin-only><h2>Settlement history</h2><div id="settlements" class="empty">admin login required</div></section>
<section data-tab="admin"><h2>Tracker hive</h2><div id="hive" class="empty">loading…</div></section>
<section data-tab="admin" class="wide"><h2>Model placement</h2><div id="admin-model-placement" class="empty">admin login required</div></section>
<section data-tab="admin" class="wide"><h2>Model placement</h2><div id="admin-model-placement-status" class="dim">Choose a model to load or release.</div><div id="admin-model-placement" class="empty">admin login required</div></section>
<section data-tab="admin" id="admin-section"><h2>All accounts (admin)</h2><div id="admin" class="empty"></div></section>
<section data-tab="admin" data-admin-only><h2>Strikes / bans / forfeitures</h2><div id="fraud" class="empty">admin login required</div></section>
<section data-tab="admin"><h2>Client balances</h2><div id="clients" class="empty">admin login required</div></section>
@@ -1801,25 +1801,34 @@ async function requestSelectedModelLoad() {
async function requestAdminModelLoad(model) {
const result = await apiCall("/v1/models/load", "POST", { model, force: true });
if (!result.ok) return alert(result.data.error || "model load request failed");
if (!result.ok) return showAdminModelPlacementStatus(result.data.error || "model load request failed", true);
const assignment = result.data.assignment || {};
showAdminModelPlacementStatus(`Load queued on ${short(assignment.node_id || "node")} for ${model}.`);
await refreshActiveTab(true);
}
async function releaseAdminModel(model) {
if (!confirm(`Release ${model} from every serving node?`)) return;
const result = await apiCall("/v1/models/release", "POST", { model });
if (!result.ok) return alert(result.data.error || "model release request failed");
if (!result.ok) return showAdminModelPlacementStatus(result.data.error || "model release request failed", true);
showAdminModelPlacementStatus(`Release queued for ${result.data.released || 0} node(s) serving ${model}.`);
await refreshActiveTab(true);
}
function showAdminModelPlacementStatus(message, isError) {
const status = $("admin-model-placement-status");
status.textContent = message;
status.className = isError ? "bad" : "ok";
}
function renderAdminModelPlacement(models, map) {
const nodes = (map && map.nodes) || [];
const rows = ((models && models.data) || []).map(model => {
const aliases = new Set([model.id, model.hf_repo, ...(model.aliases || [])].filter(Boolean));
const serving = nodes.filter(node => aliases.has(node.model) || aliases.has(node.hf_repo)).length;
const downloaded = nodes.filter(node => (node.downloaded_models || []).some(item => aliases.has(item.model) || aliases.has(item.hf_repo))).length;
const actions = `<button class="small" onclick="requestAdminModelLoad(${JSON.stringify(model.id)})">load</button> ` +
`<button class="small" onclick="releaseAdminModel(${JSON.stringify(model.id)})"${serving ? "" : " disabled"}>release</button>`;
const downloaded = nodes.filter(node => aliases.has(node.model) || aliases.has(node.hf_repo) ||
(node.downloaded_models || []).some(item => aliases.has(item.model) || aliases.has(item.hf_repo))).length;
const actions = `<button class="small" data-admin-model-load="${esc(model.id)}">load</button> ` +
`<button class="small" data-admin-model-release="${esc(model.id)}"${serving ? "" : " disabled"}>release</button>`;
return [esc(model.name || model.id), String(serving), String(downloaded), actions];
});
$("admin-model-placement").innerHTML = rows.length
@@ -1827,6 +1836,13 @@ function renderAdminModelPlacement(models, map) {
: '<div class="empty">no model presets configured</div>';
}
$("admin-model-placement").addEventListener("click", event => {
const load = event.target.closest("[data-admin-model-load]");
const release = event.target.closest("[data-admin-model-release]");
if (load) void requestAdminModelLoad(load.dataset.adminModelLoad);
if (release) void releaseAdminModel(release.dataset.adminModelRelease);
});
function chatAuthToken() {
if (accountApiKeys.length) return accountApiKeys[0];
return null;

View File

@@ -111,6 +111,9 @@ def test_dashboard_exposes_admin_model_inventory_and_release_controls():
assert '"/v1/models/release"' in html
assert "requestAdminModelLoad" in html
assert "releaseAdminModel" in html
assert 'data-admin-model-load=' in html
assert 'data-admin-model-release=' in html
assert "admin-model-placement-status" in html
def test_network_map_includes_node_friendly_name():