Spaces:
Running
Running
| const $ = (id) => document.getElementById(id); | |
| const charts = {}; | |
| const pending = new Map(); | |
| let requestId = 0; | |
| let toastTimer = null; | |
| let lastResult = null; | |
| let lastArenaRows = []; | |
| let lastCapacityTrace = []; | |
| let lastTopologyRows = []; | |
| let lastDesignRows = []; | |
| let lastPairedStudy = null; | |
| let lastRobustStudy = null; | |
| let lastAgentRun = null; | |
| let lastAgentCompare = null; | |
| let lastAgentTtl = null; | |
| let lastAgentMemory = null; | |
| let lastAgentBudget = null; | |
| let lastAgentAffinity = null; | |
| let lastPredictiveStudy = null; | |
| let lastPredictiveAlpha = null; | |
| let lastExecutionRun = null; | |
| let lastExecutionCompare = null; | |
| let lastExecutionThreshold = null; | |
| let lastExecutionDecay = null; | |
| let lastExecutionPlanning = null; | |
| let lastExecutionHorizon = null; | |
| let lastExecutionBudget = null; | |
| let lastConsolidation = null; | |
| let lastMeasurementImport = null; | |
| let lastCalibration = null; | |
| let measurementFileContent = ""; | |
| let traceRequests = []; | |
| const COLORS = { | |
| blue: "#78a1db", | |
| blue2: "#5f86bf", | |
| steel: "#91a0b2", | |
| green: "#69c39a", | |
| amber: "#d2b36a", | |
| red: "#d97f89", | |
| purple: "#9288c7", | |
| gray: "#687585", | |
| grid: "rgba(140,155,175,.14)", | |
| }; | |
| const runtimePill = $("runtimePill"); | |
| const runtimeText = $("runtimeText"); | |
| const worker = new Worker("./worker.mjs", { type: "module" }); | |
| worker.addEventListener("message", (event) => { | |
| const data = event.data || {}; | |
| if (data.type === "ready") { | |
| runtimePill.classList.add("ready"); | |
| runtimeText.textContent = "Python runtime ready"; | |
| ["runBtn", "arenaBtn", "capacityBtn", "topologyBtn", "designBtn", "pairedStudyBtn", "robustStudyBtn", "agentRunBtn", "agentCompareBtn", "agentTtlBtn", "agentMemoryCompareBtn", "agentBudgetBtn", "agentAffinityBtn", "predictiveCompareBtn", "predictiveAlphaBtn", "execRunBtn", "execCompareBtn", "execThresholdBtn", "execDecayBtn", "execPlanningBtn", "execHorizonBtn", "execBudgetBtn", "consRunBtn"].forEach((id) => { $(id).disabled = false; }); | |
| if (measurementFileContent) $("measurementCalibrateBtn").disabled = false; | |
| syncConditionalControls(); | |
| window.setTimeout(() => document.body.classList.add("runtime-ready"), 650); | |
| return; | |
| } | |
| if (data.type === "fatal") { | |
| runtimePill.classList.add("error"); | |
| runtimeText.textContent = "Runtime failed"; | |
| console.error(data.error); | |
| return; | |
| } | |
| if (!pending.has(data.id)) return; | |
| const { resolve, reject } = pending.get(data.id); | |
| pending.delete(data.id); | |
| data.error ? reject(new Error(data.error)) : resolve(data.result); | |
| }); | |
| function callPython(action, payload) { | |
| const id = ++requestId; | |
| return new Promise((resolve, reject) => { | |
| pending.set(id, { resolve, reject }); | |
| worker.postMessage({ id, action, payload }); | |
| }); | |
| } | |
| function num(id) { return Number($(id).value); } | |
| function boolSelect(id) { return $(id).value === "on"; } | |
| function configFromUI(overrides = {}) { | |
| return { | |
| model: $("model").value, | |
| accelerator: $("accelerator").value, | |
| prefill_accelerator: $("prefillAccelerator").value, | |
| decode_accelerator: $("decodeAccelerator").value, | |
| prefill_workers: num("prefillWorkers"), | |
| decode_workers: num("decodeWorkers"), | |
| interconnect_gbps: num("interconnect"), | |
| transfer_base_ms: num("transferBase"), | |
| topology: $("topology").value, | |
| scheduler: $("scheduler").value, | |
| quantization: $("quantization").value, | |
| prefix_cache_enabled: boolSelect("prefixCache"), | |
| shared_prefix_tokens: num("sharedPrefix"), | |
| prefix_reuse_fraction: num("prefixReuse"), | |
| arrival_process: $("arrival").value, | |
| request_rate_rps: num("rate"), | |
| duration_s: num("duration"), | |
| prompt_tokens_mean: num("promptMean"), | |
| prompt_tokens_cv: num("promptCv"), | |
| output_tokens_mean: num("outputMean"), | |
| output_tokens_cv: num("outputCv"), | |
| max_batch_size: num("maxBatch"), | |
| max_batch_tokens: num("maxBatchTokens"), | |
| chunk_size: num("chunkSize"), | |
| kv_block_tokens: num("kvBlock"), | |
| burst_multiplier: num("burstMultiplier"), | |
| burst_period_s: num("burstPeriod"), | |
| seed: num("seed"), | |
| slo_ttft_ms: num("sloTtft"), | |
| slo_e2e_ms: num("sloE2e"), | |
| slo_attainment_target: 0.99, | |
| trace_requests: $("arrival").value === "trace" ? traceRequests : [], | |
| ...overrides, | |
| }; | |
| } | |
| function fmt(value, digits = 1) { | |
| if (!Number.isFinite(value)) return "N/A"; | |
| return value.toLocaleString(undefined, { maximumFractionDigits: digits }); | |
| } | |
| function pct(value, digits = 1) { return `${fmt(value * 100, digits)}%`; } | |
| function escapeHtml(value) { | |
| return String(value).replace(/[&<>'"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", "'": "'", '"': """ })[c]); | |
| } | |
| function slug(value) { | |
| return String(value).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, ""); | |
| } | |
| function stamp() { | |
| const d = new Date(); | |
| const pad = (v) => String(v).padStart(2, "0"); | |
| return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`; | |
| } | |
| function showToast(message, kind = "info", timeoutMs = 2200) { | |
| const toast = $("toast"); | |
| toast.textContent = message; | |
| toast.classList.toggle("error", kind === "error"); | |
| toast.classList.add("show"); | |
| if (toastTimer) clearTimeout(toastTimer); | |
| toastTimer = setTimeout(() => { | |
| toast.classList.remove("show", "error"); | |
| }, timeoutMs); | |
| } | |
| function reportError(context, error) { | |
| console.error(context, error); | |
| showToast(`${context}: ${error.message}`, "error", 5200); | |
| } | |
| async function copyText(text, message) { | |
| try { | |
| await navigator.clipboard.writeText(text); | |
| showToast(message); | |
| } catch { | |
| const area = document.createElement("textarea"); | |
| area.value = text; | |
| area.style.position = "fixed"; | |
| area.style.opacity = "0"; | |
| document.body.appendChild(area); | |
| area.select(); | |
| document.execCommand("copy"); | |
| area.remove(); | |
| showToast(message); | |
| } | |
| } | |
| function csvCell(value) { | |
| const text = String(value ?? ""); | |
| return /[",\n]/.test(text) ? `"${text.replaceAll('"', '""')}"` : text; | |
| } | |
| function tableText(headers, rows, separator = "\t") { | |
| return [headers, ...rows].map((row) => row.join(separator)).join("\n"); | |
| } | |
| function downloadCsv(filename, headers, rows) { | |
| const csv = [headers, ...rows].map((row) => row.map(csvCell).join(",")).join("\n"); | |
| const blob = new Blob([csv], { type: "text/csv;charset=utf-8" }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement("a"); | |
| a.href = url; | |
| a.download = filename; | |
| a.click(); | |
| URL.revokeObjectURL(url); | |
| } | |
| function downloadJson(result) { | |
| const cfg = result.config || configFromUI(); | |
| const topology = cfg.topology === "disaggregated_pd" ? `pd-${cfg.prefill_workers}p-${cfg.decode_workers}d` : `colocated-${cfg.accelerator}`; | |
| const name = `inferscale-run_${slug(cfg.model)}_${slug(topology)}_${slug(cfg.scheduler)}_${stamp()}.json`; | |
| const blob = new Blob([JSON.stringify(result, null, 2)], { type: "application/json" }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement("a"); | |
| a.href = url; | |
| a.download = name; | |
| a.click(); | |
| URL.revokeObjectURL(url); | |
| } | |
| function downloadNamedJson(prefix, result) { | |
| const blob = new Blob([JSON.stringify(result, null, 2)], { type: "application/json" }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement("a"); | |
| a.href = url; | |
| a.download = `${prefix}_${stamp()}.json`; | |
| a.click(); | |
| URL.revokeObjectURL(url); | |
| } | |
| function downloadTextFile(filename, content, mime = "text/plain;charset=utf-8") { | |
| const blob = new Blob([content], { type: mime }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement("a"); | |
| a.href = url; | |
| a.download = filename; | |
| a.click(); | |
| URL.revokeObjectURL(url); | |
| } | |
| Chart.defaults.color = "#929dab"; | |
| Chart.defaults.borderColor = COLORS.grid; | |
| Chart.defaults.font.family = getComputedStyle(document.body).fontFamily; | |
| Chart.defaults.font.size = 13; | |
| Chart.defaults.animation.duration = 0; | |
| function commonChartOptions() { | |
| return { responsive: true, maintainAspectRatio: false }; | |
| } | |
| function lineLegend() { | |
| return { | |
| labels: { | |
| usePointStyle: true, | |
| pointStyle: "line", | |
| pointStyleWidth: 26, | |
| boxWidth: 26, | |
| boxHeight: 2, | |
| padding: 14, | |
| }, | |
| }; | |
| } | |
| function pointLegend() { | |
| return { | |
| labels: { | |
| usePointStyle: true, | |
| boxWidth: 8, | |
| boxHeight: 8, | |
| padding: 14, | |
| }, | |
| }; | |
| } | |
| function destroyChart(name) { | |
| if (charts[name]) { | |
| charts[name].destroy(); | |
| delete charts[name]; | |
| } | |
| } | |
| function chartFileName(card) { | |
| const chartName = card.dataset.chartName || "figure"; | |
| if (chartName.startsWith("agent-")) { | |
| const cfg = agentConfigFromUI(); | |
| return `inferscale_${slug(chartName)}_${slug(cfg.model)}_${slug(cfg.accelerator)}_${slug(cfg.retention_policy)}_${slug(cfg.routing_policy)}_${stamp()}.png`; | |
| } | |
| const cfg = configFromUI(); | |
| const topology = cfg.topology === "disaggregated_pd" | |
| ? `pd-${cfg.prefill_workers}p-${cfg.decode_workers}d-${cfg.prefill_accelerator}-${cfg.decode_accelerator}` | |
| : `colocated-${cfg.accelerator}`; | |
| return `inferscale_${slug(chartName)}_${slug(cfg.model)}_${slug(topology)}_${slug(cfg.scheduler)}_${stamp()}.png`; | |
| } | |
| function downloadChart(card) { | |
| const canvas = card.querySelector("canvas"); | |
| const chart = Chart.getChart(canvas); | |
| if (!chart) return; | |
| const exportCanvas = document.createElement("canvas"); | |
| exportCanvas.width = canvas.width; | |
| exportCanvas.height = canvas.height; | |
| const ctx = exportCanvas.getContext("2d"); | |
| ctx.fillStyle = "#0a0f15"; | |
| ctx.fillRect(0, 0, exportCanvas.width, exportCanvas.height); | |
| ctx.drawImage(canvas, 0, 0); | |
| exportCanvas.toBlob((blob) => { | |
| if (!blob) return; | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement("a"); | |
| a.href = url; | |
| a.download = chartFileName(card); | |
| a.click(); | |
| URL.revokeObjectURL(url); | |
| showToast("Chart PNG downloaded"); | |
| }, "image/png"); | |
| } | |
| function renderSimulation(result) { | |
| lastResult = result; | |
| $("emptyState").classList.add("hidden"); | |
| $("resultContent").classList.remove("hidden"); | |
| $("exportBtn").disabled = false; | |
| $("copyResultBtn").disabled = false; | |
| const s = result.summary; | |
| const l = result.latency; | |
| const r = result.resource; | |
| const d = result.diagnostics || {}; | |
| $("mTtft").textContent = `${fmt(l.ttft_ms.p95)} ms`; | |
| $("mE2e").textContent = `${fmt(l.e2e_ms.p95)} ms`; | |
| $("mGoodput").textContent = `${fmt(s.goodput_rps, 2)} req/s`; | |
| $("mSlo").textContent = pct(s.slo_attainment); | |
| $("mReq").textContent = `${fmt(s.request_throughput_rps, 2)} req/s`; | |
| $("mKv").textContent = `${fmt(r.peak_kv_gb, 2)} GB`; | |
| const tag = $("runState"); | |
| tag.textContent = `${s.requests_completed}/${s.requests_generated} completed`; | |
| tag.className = `tag ${s.slo_attainment >= .99 && s.requests_unfinished === 0 ? "good" : "bad"}`; | |
| $("mBottleneck").textContent = d.label || "N/A"; | |
| $("mDiagnosis").textContent = d.explanation || "No simulator diagnosis available."; | |
| $("mRecommendation").textContent = d.recommendation ? `Next check: ${d.recommendation}` : ""; | |
| const e = d.evidence || {}; | |
| const evidence = [ | |
| `topology ${r.topology || result.config.topology || "colocated"}`, | |
| `busy ${pct(e.busy_fraction ?? s.busy_fraction)}`, | |
| `KV ${pct(e.peak_kv_utilization ?? r.peak_kv_utilization)}`, | |
| `TTFT pass ${pct(e.ttft_slo_attainment ?? s.ttft_slo_attainment)}`, | |
| `E2E pass ${pct(e.e2e_slo_attainment ?? s.e2e_slo_attainment)}`, | |
| `queue p95 ${fmt(e.queue_p95_ms ?? l.queue_ms.p95)} ms`, | |
| ]; | |
| if ((r.prefix_cache_hit_rate ?? 0) > 0) evidence.push(`cache hit ${pct(r.prefix_cache_hit_rate)}`, `prefill saved ${fmt(r.prefill_tokens_saved, 0)} tok`); | |
| if (r.topology === "disaggregated_pd") evidence.push( | |
| `prefill busy ${pct(r.prefill_busy_fraction)}`, | |
| `decode busy ${pct(r.decode_busy_fraction)}`, | |
| `link busy ${pct(r.transfer_busy_fraction)}`, | |
| `transfer p95 ${fmt(r.p95_transfer_ms, 2)} ms`, | |
| ); | |
| $("mEvidence").innerHTML = evidence.map((x) => `<span>${escapeHtml(x)}</span>`).join(""); | |
| destroyChart("latency"); | |
| charts.latency = new Chart($("latencyChart"), { | |
| type: "bar", | |
| data: { | |
| labels: ["TTFT p50", "TTFT p95", "E2E p50", "E2E p95", "Queue p95"], | |
| datasets: [{ | |
| label: "Milliseconds", | |
| data: [l.ttft_ms.p50, l.ttft_ms.p95, l.e2e_ms.p50, l.e2e_ms.p95, l.queue_ms.p95], | |
| backgroundColor: [COLORS.steel, COLORS.blue, "#8196b1", COLORS.blue2, COLORS.amber], | |
| }], | |
| }, | |
| options: { ...commonChartOptions(), plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true } } }, | |
| }); | |
| const timeline = result.timeline || []; | |
| const lineDatasets = [ | |
| { label: r.topology === "disaggregated_pd" ? "Prefill queue" : "Waiting", data: timeline.map((x) => ({ x: x.time_s, y: x.waiting })), borderColor: COLORS.amber, pointRadius: 0, tension: .08, yAxisID: "y" }, | |
| { label: "Decoding", data: timeline.map((x) => ({ x: x.time_s, y: x.decoding })), borderColor: COLORS.blue, pointRadius: 0, tension: .08, yAxisID: "y" }, | |
| { label: "KV GB", data: timeline.map((x) => ({ x: x.time_s, y: x.kv_used_gb })), borderColor: COLORS.green, pointRadius: 0, tension: .08, yAxisID: "y1" }, | |
| ]; | |
| if (r.topology === "disaggregated_pd") { | |
| lineDatasets.splice(1, 0, { label: "Decode queue", data: timeline.map((x) => ({ x: x.time_s, y: x.decode_ready || 0 })), borderColor: COLORS.steel, borderDash: [4, 3], pointRadius: 0, tension: .08, yAxisID: "y" }); | |
| } | |
| destroyChart("timeline"); | |
| charts.timeline = new Chart($("timelineChart"), { | |
| type: "line", | |
| data: { datasets: lineDatasets }, | |
| options: { | |
| ...commonChartOptions(), | |
| parsing: false, | |
| interaction: { mode: "nearest", intersect: false }, | |
| plugins: { legend: lineLegend() }, | |
| scales: { | |
| x: { type: "linear", title: { display: true, text: "Virtual time (s)" } }, | |
| y: { beginAtZero: true, title: { display: true, text: "Requests" } }, | |
| y1: { beginAtZero: true, position: "right", grid: { drawOnChartArea: false }, title: { display: true, text: "KV GB" } }, | |
| }, | |
| }, | |
| }); | |
| const sample = result.requests || []; | |
| destroyChart("scatter"); | |
| charts.scatter = new Chart($("scatterChart"), { | |
| type: "scatter", | |
| data: { datasets: [{ label: "Requests", data: sample.map((x) => ({ x: x.prompt_tokens, y: x.ttft_ms })), backgroundColor: "rgba(121,167,255,.55)", pointRadius: 2.2 }] }, | |
| options: { ...commonChartOptions(), plugins: { legend: { display: false } }, scales: { x: { title: { display: true, text: "Prompt tokens" } }, y: { title: { display: true, text: "TTFT (ms)" }, beginAtZero: true } } }, | |
| }); | |
| const warnings = $("warnings"); | |
| const allWarnings = [...(result.warnings || [])]; | |
| if (result.provenance?.profile_warning) allWarnings.unshift(result.provenance.profile_warning); | |
| if (allWarnings.length) { | |
| warnings.innerHTML = allWarnings.map((w) => `<div>${escapeHtml(w)}</div>`).join(""); | |
| warnings.classList.remove("hidden"); | |
| } else { | |
| warnings.classList.add("hidden"); | |
| } | |
| } | |
| $("runBtn").addEventListener("click", async () => { | |
| if ($("arrival").value === "trace" && traceRequests.length === 0) { showToast("Load a trace file first"); return; } | |
| const button = $("runBtn"); | |
| const state = $("runState"); | |
| button.disabled = true; | |
| button.textContent = "Running simulation..."; | |
| state.textContent = "Simulating..."; | |
| state.className = "tag neutral"; | |
| try { | |
| renderSimulation(await callPython("simulate", configFromUI())); | |
| } catch (error) { | |
| state.textContent = "Error"; | |
| state.className = "tag bad"; | |
| reportError("Simulation failed", error); | |
| } finally { | |
| button.textContent = "Run simulation"; | |
| button.disabled = false; | |
| } | |
| }); | |
| $("copyResultBtn").addEventListener("click", () => { if (lastResult) copyText(JSON.stringify(lastResult, null, 2), "Result JSON copied"); }); | |
| $("exportBtn").addEventListener("click", () => { if (lastResult) downloadJson(lastResult); }); | |
| function schedulerLabel(value) { | |
| return ({ | |
| static_fcfs: "Static FCFS", | |
| continuous_fcfs: "Continuous FCFS", | |
| continuous_sjf: "Continuous SJF", | |
| continuous_slo: "Continuous SLO", | |
| chunked_slo: "Chunked SLO", | |
| })[value] || value; | |
| } | |
| function arenaTableRows(rows) { | |
| return rows.map((r) => [schedulerLabel(r.scheduler), `${fmt(r.goodput_rps, 2)} req/s`, pct(r.slo_attainment), `${fmt(r.p95_ttft_ms)} ms`, `${fmt(r.p95_e2e_ms)} ms`, pct(r.peak_kv_utilization), fmt(r.unfinished, 0), r.bottleneck || "N/A"]); | |
| } | |
| function renderArena(rows) { | |
| lastArenaRows = rows; | |
| $("arenaEmpty").classList.add("hidden"); | |
| $("arenaContent").classList.remove("hidden"); | |
| $("arenaCopyBtn").disabled = false; | |
| $("arenaCsvBtn").disabled = false; | |
| $("arenaRows").innerHTML = rows.map((r, index) => `<tr><td>${escapeHtml(schedulerLabel(r.scheduler))}${index === 0 ? '<span class="best-label">Best</span>' : ""}</td><td>${fmt(r.goodput_rps, 2)} req/s</td><td>${pct(r.slo_attainment)}</td><td>${fmt(r.p95_ttft_ms)} ms</td><td>${fmt(r.p95_e2e_ms)} ms</td><td>${pct(r.peak_kv_utilization)}</td><td>${fmt(r.unfinished, 0)}</td><td>${escapeHtml(r.bottleneck || "N/A")}</td></tr>`).join(""); | |
| destroyChart("arena"); | |
| charts.arena = new Chart($("arenaChart"), { | |
| type: "bar", | |
| data: { labels: rows.map((r) => schedulerLabel(r.scheduler)), datasets: [{ label: "Goodput (req/s)", data: rows.map((r) => r.goodput_rps), backgroundColor: COLORS.blue }, { label: "Raw throughput (req/s)", data: rows.map((r) => r.request_throughput_rps), backgroundColor: "#53677f" }] }, | |
| options: { ...commonChartOptions(), scales: { y: { beginAtZero: true } } }, | |
| }); | |
| } | |
| $("arenaBtn").addEventListener("click", async () => { | |
| const button = $("arenaBtn"); | |
| button.disabled = true; | |
| button.textContent = "Comparing..."; | |
| try { renderArena((await callPython("compare", { config: configFromUI() })).rows); } | |
| catch (error) { reportError("Scheduler comparison failed", error); } | |
| finally { button.disabled = false; button.textContent = "Compare schedulers"; } | |
| }); | |
| const arenaHeaders = ["Scheduler", "Goodput", "SLO attainment", "p95 TTFT", "p95 E2E", "KV peak", "Unfinished", "Diagnosis"]; | |
| $("arenaCopyBtn").addEventListener("click", () => copyText(tableText(arenaHeaders, arenaTableRows(lastArenaRows)), "Scheduler table copied")); | |
| $("arenaCsvBtn").addEventListener("click", () => downloadCsv(`inferscale_scheduler-arena_${stamp()}.csv`, arenaHeaders, arenaTableRows(lastArenaRows))); | |
| function capacityTableRows(trace) { | |
| return trace.map((r) => [ | |
| `${fmt(r.rate_rps, 2)} req/s`, | |
| r.passed ? "PASS" : "FAIL", | |
| pct(r.slo_attainment), | |
| pct(r.slo_attainment_min), | |
| pct(r.target ?? num("targetSlo")), | |
| `${pct(r.slo_attainment_min)} - ${pct(r.slo_attainment_max)}`, | |
| `${fmt(r.goodput_rps, 2)} req/s`, | |
| `${fmt(r.p95_ttft_ms)} ms`, | |
| `${fmt(r.p95_e2e_ms)} ms`, | |
| ]); | |
| } | |
| function renderCapacity(result) { | |
| lastCapacityTrace = result.trace || []; | |
| $("plannerEmpty").classList.add("hidden"); | |
| $("plannerContent").classList.remove("hidden"); | |
| $("capacityCopyBtn").disabled = false; | |
| $("capacityCsvBtn").disabled = false; | |
| $("pCapacity").textContent = `${fmt(result.capacity_rps, 2)} req/s`; | |
| $("pRecommended").textContent = `${fmt(result.recommended_rps, 2)} req/s`; | |
| $("pHeadroom").textContent = pct(result.headroom ?? num("headroom")); | |
| $("pStatus").textContent = result.status.replaceAll("_", " "); | |
| const state = $("plannerState"); | |
| state.textContent = result.status === "ok" ? "Search complete" : result.status.replaceAll("_", " "); | |
| state.className = `tag ${result.capacity_rps > 0 ? "good" : "bad"}`; | |
| $("capacityRows").innerHTML = lastCapacityTrace.map((r) => `<tr><td>${fmt(r.rate_rps, 2)} req/s</td><td class="${r.passed ? "pass" : "fail"}">${r.passed ? "PASS" : "FAIL"}</td><td>${pct(r.slo_attainment)}</td><td>${pct(r.slo_attainment_min)}</td><td>${pct(r.target ?? num("targetSlo"))}</td><td>${pct(r.slo_attainment_min)} - ${pct(r.slo_attainment_max)}</td><td>${fmt(r.goodput_rps, 2)} req/s</td><td>${fmt(r.p95_ttft_ms)} ms</td><td>${fmt(r.p95_e2e_ms)} ms</td></tr>`).join(""); | |
| if (!lastCapacityTrace.length) return; | |
| const target = num("targetSlo"); | |
| const mean = lastCapacityTrace.map((r) => ({ x: r.rate_rps, y: r.slo_attainment })); | |
| const worst = lastCapacityTrace.map((r) => ({ x: r.rate_rps, y: r.slo_attainment_min })); | |
| const xs = lastCapacityTrace.map((r) => r.rate_rps); | |
| const minX = Math.min(...xs); | |
| const maxX = Math.max(...xs); | |
| destroyChart("capacity"); | |
| charts.capacity = new Chart($("capacityChart"), { | |
| type: "line", | |
| data: { datasets: [ | |
| { label: "Mean SLO attainment", data: mean, borderColor: COLORS.blue, backgroundColor: COLORS.blue, fill: false, tension: .06, pointRadius: 3 }, | |
| { label: "Worst repetition", data: worst, borderColor: COLORS.amber, backgroundColor: COLORS.amber, borderDash: [5, 4], fill: false, pointRadius: 2, tension: .06 }, | |
| { label: "Target", data: [{ x: minX, y: target }, { x: maxX, y: target }], borderColor: COLORS.green, backgroundColor: COLORS.green, borderDash: [6, 5], fill: false, pointRadius: 0 }, | |
| ] }, | |
| options: { ...commonChartOptions(), parsing: false, plugins: { legend: lineLegend() }, scales: { x: { type: "linear", title: { display: true, text: "Offered load (req/s)" }, ticks: { maxTicksLimit: 8 } }, y: { min: 0, max: 1, ticks: { callback: (v) => `${Math.round(v * 100)}%` } } } }, | |
| }); | |
| } | |
| $("capacityBtn").addEventListener("click", async () => { | |
| if ($("arrival").value === "trace") { showToast("Capacity search requires a rate-driven workload"); return; } | |
| const button = $("capacityBtn"); | |
| const state = $("plannerState"); | |
| button.disabled = true; | |
| button.textContent = "Searching..."; | |
| state.textContent = "Running simulations..."; | |
| state.className = "tag neutral"; | |
| try { | |
| renderCapacity(await callPython("capacity", { | |
| config: configFromUI({ slo_attainment_target: num("targetSlo") }), | |
| min_rate: num("minRate"), | |
| max_rate: num("maxRate"), | |
| iterations: num("searchIter"), | |
| repetitions: num("repetitions"), | |
| headroom: num("headroom"), | |
| })); | |
| } catch (error) { | |
| state.textContent = "Error"; | |
| state.className = "tag bad"; | |
| reportError("Capacity search failed", error); | |
| } finally { | |
| button.disabled = false; | |
| button.textContent = "Find sustainable capacity"; | |
| } | |
| }); | |
| const capacityHeaders = ["Rate", "Pass", "Mean SLO", "Worst repetition", "Target", "SLO range", "Goodput", "p95 TTFT", "p95 E2E"]; | |
| $("capacityCopyBtn").addEventListener("click", () => copyText(tableText(capacityHeaders, capacityTableRows(lastCapacityTrace)), "Capacity table copied")); | |
| $("capacityCsvBtn").addEventListener("click", () => downloadCsv(`inferscale_capacity-trace_${stamp()}.csv`, capacityHeaders, capacityTableRows(lastCapacityTrace))); | |
| function scenarioLabel(value) { | |
| return ({ | |
| colocated: "Colocated", | |
| colocated_cache: "Colocated + prefix cache", | |
| disaggregated_pd: "P/D disaggregated", | |
| disaggregated_pd_cache: "P/D + prefix cache", | |
| })[value] || value; | |
| } | |
| function topologyTableRows(rows) { | |
| return rows.map((r) => [ | |
| scenarioLabel(r.scenario), | |
| fmt(r.accelerator_instances, 0), | |
| `${fmt(r.goodput_rps, 2)} req/s`, | |
| `${fmt(r.goodput_per_accelerator, 2)} req/s/GPU`, | |
| pct(r.slo_attainment), | |
| `${fmt(r.p95_ttft_ms)} ms`, | |
| `${fmt(r.p95_e2e_ms)} ms`, | |
| `${fmt(r.p95_transfer_ms, 2)} ms`, | |
| pct(r.prefix_hit_rate), | |
| `${fmt(r.prefill_tokens_saved, 0)} tok`, | |
| r.bottleneck || "N/A", | |
| ]); | |
| } | |
| function renderTopology(rows) { | |
| lastTopologyRows = rows; | |
| $("topologyEmpty").classList.add("hidden"); | |
| $("topologyContent").classList.remove("hidden"); | |
| $("topologyCopyBtn").disabled = false; | |
| $("topologyCsvBtn").disabled = false; | |
| $("topologyRows").innerHTML = rows.map((r, index) => `<tr><td>${escapeHtml(scenarioLabel(r.scenario))}${index === 0 ? '<span class="best-label">Best</span>' : ""}</td><td>${fmt(r.accelerator_instances, 0)}</td><td>${fmt(r.goodput_rps, 2)} req/s</td><td>${pct(r.slo_attainment)}</td><td>${fmt(r.p95_ttft_ms)} ms</td><td>${fmt(r.p95_e2e_ms)} ms</td><td>${fmt(r.p95_transfer_ms, 2)} ms</td><td>${pct(r.prefix_hit_rate)}</td><td>${fmt(r.prefill_tokens_saved, 0)} tok</td><td>${escapeHtml(r.bottleneck || "N/A")}</td></tr>`).join(""); | |
| const palette = [COLORS.blue, COLORS.green, COLORS.amber, COLORS.red]; | |
| destroyChart("topology"); | |
| charts.topology = new Chart($("topologyChart"), { | |
| type: "scatter", | |
| data: { datasets: rows.map((r, i) => ({ label: scenarioLabel(r.scenario), data: [{ x: r.p95_ttft_ms, y: r.goodput_rps }], backgroundColor: palette[i % palette.length], borderColor: palette[i % palette.length], pointRadius: 6, pointHoverRadius: 8 })) }, | |
| options: { ...commonChartOptions(), plugins: { legend: pointLegend() }, scales: { x: { title: { display: true, text: "p95 TTFT (ms)" }, beginAtZero: true }, y: { title: { display: true, text: "Goodput (req/s)" }, beginAtZero: true } } }, | |
| }); | |
| } | |
| $("topologyBtn").addEventListener("click", async () => { | |
| const button = $("topologyBtn"); | |
| button.disabled = true; | |
| button.textContent = "Comparing..."; | |
| try { renderTopology((await callPython("topology_compare", { config: configFromUI() })).rows); } | |
| catch (error) { reportError("Topology comparison failed", error); } | |
| finally { button.disabled = false; button.textContent = "Compare 4 scenarios"; } | |
| }); | |
| const topologyHeaders = ["Scenario", "GPU instances", "Goodput", "Goodput / GPU", "SLO attainment", "p95 TTFT", "p95 E2E", "p95 KV transfer", "Cache hit", "Prefill saved", "Diagnosis"]; | |
| $("topologyCopyBtn").addEventListener("click", () => copyText(tableText(topologyHeaders, topologyTableRows(lastTopologyRows)), "Topology table copied")); | |
| $("topologyCsvBtn").addEventListener("click", () => downloadCsv(`inferscale_topology-cache-study_${stamp()}.csv`, topologyHeaders, topologyTableRows(lastTopologyRows))); | |
| function designTableRows(rows) { | |
| return rows.map((r) => [ | |
| r.label, | |
| r.pareto ? "YES" : "NO", | |
| r.efficiency_pareto ? "YES" : "NO", | |
| r.slo_pass ? "PASS" : "FAIL", | |
| fmt(r.accelerator_instances, 0), | |
| `${fmt(r.goodput_rps, 2)} req/s`, | |
| `${fmt(r.goodput_per_accelerator, 2)} req/s/GPU`, | |
| `${fmt(r.p95_ttft_ms)} ms`, | |
| `${fmt(r.p95_e2e_ms)} ms`, | |
| pct(r.peak_kv_utilization), | |
| r.bottleneck || "N/A", | |
| ]); | |
| } | |
| function renderDesign(result) { | |
| lastDesignRows = result.rows || []; | |
| $("designEmpty").classList.add("hidden"); | |
| $("designContent").classList.remove("hidden"); | |
| $("designCopyBtn").disabled = false; | |
| $("designCsvBtn").disabled = false; | |
| $("dCandidates").textContent = fmt(result.candidate_count, 0); | |
| $("dPareto").textContent = fmt(result.pareto_count, 0); | |
| $("dEfficiencyPareto").textContent = fmt(result.efficiency_pareto_count, 0); | |
| $("designRows").innerHTML = lastDesignRows.map((r) => `<tr><td>${escapeHtml(r.label)}${r.pareto ? '<span class="pareto-label">Perf</span>' : ""}${r.efficiency_pareto ? '<span class="pareto-label">Eff</span>' : ""}</td><td class="${r.pareto ? "pass" : ""}">${r.pareto ? "YES" : "NO"}</td><td class="${r.efficiency_pareto ? "pass" : ""}">${r.efficiency_pareto ? "YES" : "NO"}</td><td class="${r.slo_pass ? "pass" : "fail"}">${r.slo_pass ? "PASS" : "FAIL"}</td><td>${fmt(r.accelerator_instances, 0)}</td><td>${fmt(r.goodput_rps, 2)} req/s</td><td>${fmt(r.goodput_per_accelerator, 2)} req/s/GPU</td><td>${fmt(r.p95_ttft_ms)} ms</td><td>${fmt(r.p95_e2e_ms)} ms</td><td>${pct(r.peak_kv_utilization)}</td><td>${escapeHtml(r.bottleneck || "N/A")}</td></tr>`).join(""); | |
| const passPoints = lastDesignRows.filter((r) => r.slo_pass).map((r) => ({ x: r.p95_ttft_ms, y: r.goodput_rps })); | |
| const failPoints = lastDesignRows.filter((r) => !r.slo_pass).map((r) => ({ x: r.p95_ttft_ms, y: r.goodput_rps })); | |
| const frontier = lastDesignRows.filter((r) => r.pareto).sort((a, b) => a.p95_ttft_ms - b.p95_ttft_ms).map((r) => ({ x: r.p95_ttft_ms, y: r.goodput_rps })); | |
| destroyChart("design"); | |
| charts.design = new Chart($("designChart"), { | |
| type: "scatter", | |
| data: { datasets: [ | |
| { label: "SLO pass", data: passPoints, backgroundColor: COLORS.green, borderColor: COLORS.green, pointRadius: 4 }, | |
| { label: "SLO fail", data: failPoints, backgroundColor: COLORS.gray, borderColor: COLORS.gray, pointRadius: 4 }, | |
| { type: "line", label: "Performance frontier", pointStyle: "line", data: frontier, borderColor: COLORS.blue, backgroundColor: COLORS.blue, pointBackgroundColor: COLORS.blue, pointRadius: 5, fill: false, tension: 0 }, | |
| ] }, | |
| options: { ...commonChartOptions(), parsing: false, plugins: { legend: pointLegend() }, scales: { x: { title: { display: true, text: "p95 TTFT (ms)" }, beginAtZero: true }, y: { title: { display: true, text: "Goodput (req/s)" }, beginAtZero: true } } }, | |
| }); | |
| const efficiencyPass = lastDesignRows.filter((r) => r.slo_pass).map((r) => ({ x: r.p95_ttft_ms, y: r.goodput_per_accelerator })); | |
| const efficiencyFail = lastDesignRows.filter((r) => !r.slo_pass).map((r) => ({ x: r.p95_ttft_ms, y: r.goodput_per_accelerator })); | |
| const efficiencyFrontier = lastDesignRows.filter((r) => r.efficiency_pareto).sort((a, b) => a.p95_ttft_ms - b.p95_ttft_ms).map((r) => ({ x: r.p95_ttft_ms, y: r.goodput_per_accelerator })); | |
| destroyChart("designEfficiency"); | |
| charts.designEfficiency = new Chart($("designEfficiencyChart"), { | |
| type: "scatter", | |
| data: { datasets: [ | |
| { label: "SLO pass", data: efficiencyPass, backgroundColor: COLORS.green, borderColor: COLORS.green, pointRadius: 4 }, | |
| { label: "SLO fail", data: efficiencyFail, backgroundColor: COLORS.gray, borderColor: COLORS.gray, pointRadius: 4 }, | |
| { type: "line", label: "Efficiency frontier", pointStyle: "line", data: efficiencyFrontier, borderColor: COLORS.amber, backgroundColor: COLORS.amber, pointBackgroundColor: COLORS.amber, pointRadius: 5, fill: false, tension: 0 }, | |
| ] }, | |
| options: { ...commonChartOptions(), parsing: false, plugins: { legend: pointLegend() }, scales: { x: { title: { display: true, text: "p95 TTFT (ms)" }, beginAtZero: true }, y: { title: { display: true, text: "Goodput / accelerator (req/s/GPU)" }, beginAtZero: true } } }, | |
| }); | |
| } | |
| $("designBtn").addEventListener("click", async () => { | |
| const button = $("designBtn"); | |
| button.disabled = true; | |
| button.textContent = "Exploring..."; | |
| try { renderDesign(await callPython("design_space", { config: configFromUI(), include_disaggregated: $("includePd").checked })); } | |
| catch (error) { reportError("Design-space sweep failed", error); } | |
| finally { button.disabled = false; button.textContent = "Explore design space"; } | |
| }); | |
| const designHeaders = ["Candidate", "Perf Pareto", "Efficiency Pareto", "SLO pass", "GPU instances", "Goodput", "Goodput / GPU", "p95 TTFT", "p95 E2E", "KV peak", "Diagnosis"]; | |
| $("designCopyBtn").addEventListener("click", () => copyText(tableText(designHeaders, designTableRows(lastDesignRows)), "Design table copied")); | |
| $("designCsvBtn").addEventListener("click", () => downloadCsv(`inferscale_design-space_${stamp()}.csv`, designHeaders, designTableRows(lastDesignRows))); | |
| function studyValue(metric, value) { | |
| if (metric === "slo_attainment") return pct(value); | |
| if (metric === "goodput_rps") return `${fmt(value, 3)} req/s`; | |
| return `${fmt(value, 2)} ms`; | |
| } | |
| function studyDelta(metric, value) { | |
| if (metric === "slo_attainment") return `${value >= 0 ? "+" : ""}${fmt(value * 100, 2)} pp`; | |
| if (metric === "goodput_rps") return `${value >= 0 ? "+" : ""}${fmt(value, 3)} req/s`; | |
| return `${value >= 0 ? "+" : ""}${fmt(value, 2)} ms`; | |
| } | |
| function pairedTableRows(result) { | |
| return (result?.metrics || []).map((m) => [ | |
| m.label, | |
| studyValue(m.metric, m.baseline_mean), | |
| studyValue(m.metric, m.treatment_mean), | |
| studyDelta(m.metric, m.delta_mean), | |
| `${studyDelta(m.metric, m.delta_ci95_low)} to ${studyDelta(m.metric, m.delta_ci95_high)}`, | |
| `${m.relative_change_pct >= 0 ? "+" : ""}${fmt(m.relative_change_pct, 2)}%`, | |
| pct(m.treatment_win_rate), | |
| m.ci_excludes_zero ? "YES" : "NO", | |
| ]); | |
| } | |
| function renderPairedStudy(result) { | |
| lastPairedStudy = result; | |
| $("pairedEmpty").classList.add("hidden"); | |
| $("pairedContent").classList.remove("hidden"); | |
| ["pairedCopyBtn", "pairedCsvBtn", "pairedJsonBtn"].forEach((id) => { $(id).disabled = false; }); | |
| const state = $("pairedState"); | |
| state.textContent = `${result.repetitions} paired runs`; | |
| state.className = "tag good"; | |
| let improvements = 0; | |
| let regressions = 0; | |
| for (const m of result.metrics) { | |
| if (!m.ci_excludes_zero) continue; | |
| const favorable = m.preferred_direction === "higher" ? m.delta_ci95_low > 0 : m.delta_ci95_high < 0; | |
| favorable ? improvements++ : regressions++; | |
| } | |
| const conclusion = improvements && !regressions | |
| ? `${improvements} metric${improvements === 1 ? "" : "s"} show a directional treatment improvement with a 95% bootstrap interval excluding zero.` | |
| : regressions && !improvements | |
| ? `${regressions} metric${regressions === 1 ? "" : "s"} show a directional treatment regression with a 95% bootstrap interval excluding zero.` | |
| : improvements || regressions | |
| ? "The treatment shows mixed statistically separated effects across metrics; inspect the latency/throughput trade-off rather than declaring one winner." | |
| : "None of the paired metric intervals excludes zero; this workload does not support a stable directional conclusion at the chosen repetition count."; | |
| $("pairedSummary").innerHTML = `<strong>${escapeHtml(result.baseline_label)} vs ${escapeHtml(result.treatment_label)}.</strong> ${result.repetitions} common-seed repetitions with ${result.bootstrap_samples} bootstrap resamples. ${escapeHtml(conclusion)}`; | |
| $("pairedRows").innerHTML = result.metrics.map((m) => { | |
| const favorable = m.preferred_direction === "higher" ? m.delta_ci95_low > 0 : m.delta_ci95_high < 0; | |
| const separated = m.ci_excludes_zero; | |
| return `<tr><td>${escapeHtml(m.label)}</td><td>${studyValue(m.metric, m.baseline_mean)}</td><td>${studyValue(m.metric, m.treatment_mean)}</td><td>${studyDelta(m.metric, m.delta_mean)}</td><td>${studyDelta(m.metric, m.delta_ci95_low)} to ${studyDelta(m.metric, m.delta_ci95_high)}</td><td>${m.relative_change_pct >= 0 ? "+" : ""}${fmt(m.relative_change_pct, 2)}%</td><td>${pct(m.treatment_win_rate)}</td><td class="${separated ? (favorable ? "pass" : "fail") : ""}">${separated ? "YES" : "NO"}</td></tr>`; | |
| }).join(""); | |
| const effects = result.metrics.map((m) => m.relative_change_pct * (m.preferred_direction === "higher" ? 1 : -1)); | |
| destroyChart("paired"); | |
| charts.paired = new Chart($("pairedChart"), { | |
| type: "bar", | |
| data: { labels: result.metrics.map((m) => m.label), datasets: [{ label: "Relative improvement (%)", data: effects, backgroundColor: effects.map((v) => v >= 0 ? COLORS.green : COLORS.red) }] }, | |
| options: { ...commonChartOptions(), plugins: { legend: { display: false }, tooltip: { callbacks: { label: (ctx) => `${fmt(ctx.raw, 2)}% (positive = treatment better)` } } }, scales: { y: { title: { display: true, text: "Relative improvement (%)" } } } }, | |
| }); | |
| } | |
| const pairedHeaders = ["Metric", "Baseline mean", "Treatment mean", "Mean delta", "95% bootstrap CI", "Relative change", "Treatment win rate", "CI excludes zero"]; | |
| $("pairedStudyBtn").addEventListener("click", async () => { | |
| const button = $("pairedStudyBtn"); | |
| button.disabled = true; | |
| button.textContent = "Running paired study..."; | |
| $("pairedState").textContent = "Running..."; | |
| $("pairedState").className = "tag neutral"; | |
| try { | |
| renderPairedStudy(await callPython("paired_study", { | |
| config: configFromUI(), study: $("studyPreset").value, | |
| repetitions: num("studyReps"), bootstrap_samples: num("studyBootstrap"), | |
| })); | |
| } catch (error) { | |
| $("pairedState").textContent = "Error"; | |
| $("pairedState").className = "tag bad"; | |
| reportError("Paired study failed", error); | |
| } finally { | |
| button.disabled = false; | |
| button.textContent = "Run paired study"; | |
| } | |
| }); | |
| $("pairedCopyBtn").addEventListener("click", () => { if (lastPairedStudy) copyText(tableText(pairedHeaders, pairedTableRows(lastPairedStudy)), "Paired study copied"); }); | |
| $("pairedCsvBtn").addEventListener("click", () => { if (lastPairedStudy) downloadCsv(`inferscale_paired-study_${slug(lastPairedStudy.study)}_${stamp()}.csv`, pairedHeaders, pairedTableRows(lastPairedStudy)); }); | |
| $("pairedJsonBtn").addEventListener("click", () => { if (lastPairedStudy) downloadNamedJson(`inferscale_paired-study_${slug(lastPairedStudy.study)}`, lastPairedStudy); }); | |
| function robustTableRows(result) { | |
| return (result?.rows || []).map((r) => [ | |
| r.sample, | |
| fmt(r.prefill_scale, 3), fmt(r.decode_scale, 3), fmt(r.transfer_scale, 3), | |
| pct(r.baseline.slo_attainment), pct(r.treatment.slo_attainment), | |
| studyDelta("goodput_rps", r.treatment.goodput_rps - r.baseline.goodput_rps), | |
| studyDelta("p95_ttft_ms", r.treatment.p95_ttft_ms - r.baseline.p95_ttft_ms), | |
| studyDelta("p95_e2e_ms", r.treatment.p95_e2e_ms - r.baseline.p95_e2e_ms), | |
| ]); | |
| } | |
| function renderRobustStudy(result) { | |
| lastRobustStudy = result; | |
| $("robustEmpty").classList.add("hidden"); | |
| $("robustContent").classList.remove("hidden"); | |
| ["robustCopyBtn", "robustCsvBtn", "robustJsonBtn"].forEach((id) => { $(id).disabled = false; }); | |
| const s = result.summary; | |
| $("rTtftWins").textContent = pct(s.treatment_ttft_win_fraction); | |
| $("rGoodputWins").textContent = pct(s.treatment_goodput_win_fraction); | |
| $("rTreatmentPass").textContent = pct(s.treatment_slo_pass_fraction); | |
| $("rBaselinePass").textContent = pct(s.baseline_slo_pass_fraction); | |
| $("robustState").textContent = `${result.samples} perturbations`; | |
| $("robustState").className = "tag good"; | |
| const ttftStable = s.treatment_ttft_win_fraction >= .8 || s.treatment_ttft_win_fraction <= .2; | |
| const goodputStable = s.treatment_goodput_win_fraction >= .8 || s.treatment_goodput_win_fraction <= .2; | |
| const stability = ttftStable && goodputStable ? "The directional conclusion is comparatively stable across the tested perturbations." : "At least one conclusion changes frequently under profile perturbation; treat the apparent winner as calibration-sensitive."; | |
| $("robustSummary").innerHTML = `<strong>${escapeHtml(result.baseline_label)} vs ${escapeHtml(result.treatment_label)}.</strong> ${result.samples} shared perturbations within +/-${fmt(result.uncertainty * 100, 0)}% of the analytical prefill/decode/transfer timing proxies. ${escapeHtml(stability)} This is sensitivity analysis, not a confidence interval over real GPUs.`; | |
| $("robustRows").innerHTML = result.rows.map((r) => `<tr><td>${r.sample}</td><td>${fmt(r.prefill_scale, 3)}</td><td>${fmt(r.decode_scale, 3)}</td><td>${fmt(r.transfer_scale, 3)}</td><td>${pct(r.baseline.slo_attainment)}</td><td>${pct(r.treatment.slo_attainment)}</td><td>${studyDelta("goodput_rps", r.treatment.goodput_rps - r.baseline.goodput_rps)}</td><td>${studyDelta("p95_ttft_ms", r.treatment.p95_ttft_ms - r.baseline.p95_ttft_ms)}</td><td>${studyDelta("p95_e2e_ms", r.treatment.p95_e2e_ms - r.baseline.p95_e2e_ms)}</td></tr>`).join(""); | |
| } | |
| const robustHeaders = ["Sample", "Prefill scale", "Decode scale", "Transfer scale", "Baseline SLO", "Treatment SLO", "Goodput delta", "TTFT delta", "E2E delta"]; | |
| $("robustStudyBtn").addEventListener("click", async () => { | |
| const button = $("robustStudyBtn"); | |
| button.disabled = true; | |
| button.textContent = "Stress-testing..."; | |
| $("robustState").textContent = "Running..."; | |
| $("robustState").className = "tag neutral"; | |
| try { | |
| renderRobustStudy(await callPython("robustness_study", { | |
| config: configFromUI(), study: $("studyPreset").value, | |
| samples: num("robustSamples"), uncertainty: num("robustUncertainty"), | |
| })); | |
| } catch (error) { | |
| $("robustState").textContent = "Error"; | |
| $("robustState").className = "tag bad"; | |
| reportError("Sensitivity study failed", error); | |
| } finally { | |
| button.disabled = false; | |
| button.textContent = "Stress-test selected hypothesis"; | |
| } | |
| }); | |
| $("robustCopyBtn").addEventListener("click", () => { if (lastRobustStudy) copyText(tableText(robustHeaders, robustTableRows(lastRobustStudy)), "Robustness table copied"); }); | |
| $("robustCsvBtn").addEventListener("click", () => { if (lastRobustStudy) downloadCsv(`inferscale_robustness_${slug(lastRobustStudy.study)}_${stamp()}.csv`, robustHeaders, robustTableRows(lastRobustStudy)); }); | |
| $("robustJsonBtn").addEventListener("click", () => { if (lastRobustStudy) downloadNamedJson(`inferscale_robustness_${slug(lastRobustStudy.study)}`, lastRobustStudy); }); | |
| function agentConfigFromUI(overrides = {}) { | |
| return { | |
| model: $("agentModel").value, | |
| accelerator: $("agentAccelerator").value, | |
| quantization: $("agentQuantization").value, | |
| replicas: num("agentReplicas"), | |
| session_rate_rps: num("agentRate"), | |
| duration_s: num("agentDuration"), | |
| turns_mean: num("agentTurns"), | |
| turns_cv: num("agentTurnsCv"), | |
| initial_prompt_tokens_mean: num("agentInitialPrompt"), | |
| append_tokens_mean: num("agentAppend"), | |
| output_tokens_mean: num("agentOutput"), | |
| token_cv: num("agentTokenCv"), | |
| output_tokens_cv: num("agentTokenCv"), | |
| tool_gap_mean_s: num("agentToolGap"), | |
| tool_gap_cv: num("agentToolGapCv"), | |
| seed: num("agentSeed"), | |
| retention_policy: $("agentRetention").value, | |
| routing_policy: $("agentRouting").value, | |
| kv_ttl_s: num("agentTtl"), | |
| kv_memory_fraction: num("agentKvFraction"), | |
| affinity_slack_ms: num("agentAffinitySlack"), | |
| gap_aware_threshold_s: num("agentGapThreshold"), | |
| host_memory_gb: num("agentHostMemory"), | |
| host_bandwidth_gbps: num("agentHostBandwidth"), | |
| host_transfer_base_ms: num("agentHostBase"), | |
| adaptive_predictor_scope: $("agentPredictorScope").value, | |
| adaptive_alpha: num("agentPredictorAlpha"), | |
| adaptive_min_observations: num("agentPredictorMin"), | |
| slo_turn_ttft_ms: num("agentTtftSlo"), | |
| slo_session_e2e_ms: num("agentSessionSlo"), | |
| ...overrides, | |
| }; | |
| } | |
| function renderAgentRun(result) { | |
| lastAgentRun = result; | |
| $("agentRunEmpty").classList.add("hidden"); | |
| $("agentRunContent").classList.remove("hidden"); | |
| ["agentRunCopyJson", "agentRunJson"].forEach((id) => { $(id).disabled = false; }); | |
| const l = result.latency; | |
| const r = result.resource; | |
| const sm = result.summary; | |
| $("agentTtft").textContent = `${fmt(l.turn_ttft_ms.p95)} ms`; | |
| $("agentSessionE2e").textContent = `${fmt(l.session_e2e_ms.p95)} ms`; | |
| $("agentSessionSloValue").textContent = pct(sm.session_slo_attainment); | |
| $("agentCacheHit").textContent = pct(r.cross_turn_cache_hit_rate); | |
| $("agentRecompute").textContent = `${fmt(r.recomputed_history_tokens, 0)} tok`; | |
| $("agentPeakKv").textContent = `${fmt(r.peak_kv_gb, 3)} GB`; | |
| $("agentHostMeanKv").textContent = `${fmt(r.mean_host_kv_gb, 3)} GB`; | |
| $("agentHostTransfer").textContent = `${fmt(r.p95_host_transfer_ms)} ms`; | |
| const state = $("agentRunState"); | |
| state.textContent = `${sm.sessions_completed}/${sm.sessions_generated} sessions`; | |
| state.className = `tag ${sm.sessions_completed === sm.sessions_generated && sm.turns_failed === 0 ? "good" : "bad"}`; | |
| $("agentRunSummary").innerHTML = `<strong>${escapeHtml(result.config.retention_policy)} retention + ${escapeHtml(result.config.routing_policy.replaceAll("_", " "))} routing.</strong> ${sm.turns_completed} turns completed across ${sm.sessions_completed} sessions. Reuse hit ${pct(r.cross_turn_cache_hit_rate)} of eligible turns; ${pct(r.host_cache_hit_rate)} came from the host tier. ${fmt(r.recomputed_history_tokens, 0)} history tokens were recomputed. HBM and host residency are simulator-side memory-time accounting metrics.`; | |
| const timeline = result.timeline || []; | |
| destroyChart("agentTimeline"); | |
| charts.agentTimeline = new Chart($("agentTimelineChart"), { | |
| type: "line", | |
| data: { | |
| datasets: [ | |
| { label: "HBM KV GB", data: timeline.map((x) => ({ x: x.time_s, y: x.kv_used_gb })), borderColor: COLORS.green, backgroundColor: COLORS.green, pointRadius: 0, tension: .08, yAxisID: "yKv" }, | |
| { label: "Host KV GB", data: timeline.map((x) => ({ x: x.time_s, y: x.host_kv_gb || 0 })), borderColor: COLORS.steel, backgroundColor: COLORS.steel, pointRadius: 0, borderDash: [6, 4], tension: .08, yAxisID: "yKv" }, | |
| { label: "Busy replicas", data: timeline.map((x) => ({ x: x.time_s, y: x.busy_replicas })), borderColor: COLORS.blue, backgroundColor: COLORS.blue, pointRadius: 0, tension: .08, yAxisID: "yCount" }, | |
| { label: "Queued turns", data: timeline.map((x) => ({ x: x.time_s, y: x.queued_turns })), borderColor: COLORS.amber, backgroundColor: COLORS.amber, pointRadius: 0, tension: .08, yAxisID: "yCount" }, | |
| ], | |
| }, | |
| options: { ...commonChartOptions(), parsing: false, plugins: { legend: lineLegend() }, scales: { x: { type: "linear", title: { display: true, text: "Virtual time (s)" } }, yCount: { position: "left", beginAtZero: true, title: { display: true, text: "Replicas / queued turns" } }, yKv: { position: "right", beginAtZero: true, grid: { drawOnChartArea: false }, title: { display: true, text: "KV GB" } } } }, | |
| }); | |
| destroyChart("agentTurn"); | |
| charts.agentTurn = new Chart($("agentTurnChart"), { | |
| type: "scatter", | |
| data: { datasets: [{ label: "Turn", data: (result.turns || []).map((x) => ({ x: x.turn_index, y: x.ttft_ms })), backgroundColor: COLORS.blue, pointRadius: 3, pointHoverRadius: 5 }] }, | |
| options: { ...commonChartOptions(), plugins: { legend: { display: false } }, scales: { x: { type: "linear", ticks: { precision: 0 }, title: { display: true, text: "Turn index" } }, y: { beginAtZero: true, title: { display: true, text: "TTFT (ms)" } } } }, | |
| }); | |
| } | |
| $("agentRunBtn").addEventListener("click", async () => { | |
| const button = $("agentRunBtn"); | |
| button.disabled = true; | |
| button.textContent = "Running stateful simulation..."; | |
| $("agentRunState").textContent = "Running..."; | |
| $("agentRunState").className = "tag neutral"; | |
| try { renderAgentRun(await callPython("agent_simulate", { config: agentConfigFromUI() })); } | |
| catch (error) { $("agentRunState").textContent = "Error"; $("agentRunState").className = "tag bad"; reportError("Agent session simulation failed", error); } | |
| finally { button.disabled = false; button.textContent = "Run stateful session simulation"; } | |
| }); | |
| $("agentRunCopyJson").addEventListener("click", () => { if (lastAgentRun) copyText(JSON.stringify(lastAgentRun, null, 2), "Agent-session JSON copied"); }); | |
| $("agentRunJson").addEventListener("click", () => { if (lastAgentRun) downloadNamedJson("inferscale_agent-session", lastAgentRun); }); | |
| const agentCompareHeaders = ["Policy", "p95 turn TTFT", "p95 session E2E", "Cache hit", "Route locality", "Recomputed history", "Mean KV", "Peak KV", "HBM GB-s", "Evictions"]; | |
| function agentCompareTableRows(result) { | |
| return result.rows.map((r) => [r.label, `${fmt(r.p95_turn_ttft_ms)} ms`, `${fmt(r.p95_session_e2e_ms)} ms`, pct(r.cache_hit_rate), pct(r.routing_locality_rate), `${fmt(r.recomputed_history_tokens, 0)} tok`, `${fmt(r.mean_kv_gb, 3)} GB`, `${fmt(r.peak_kv_gb, 3)} GB`, fmt(r.hbm_gb_seconds, 3), r.pressure_evictions + r.ttl_evictions]); | |
| } | |
| function renderAgentCompare(result) { | |
| lastAgentCompare = result; | |
| $("agentCompareEmpty").classList.add("hidden"); | |
| $("agentCompareContent").classList.remove("hidden"); | |
| ["agentCompareCopy", "agentCompareCsv"].forEach((id) => { $(id).disabled = false; }); | |
| $("agentCompareRows").innerHTML = result.rows.map((r) => `<tr><td>${escapeHtml(r.label)}</td><td>${fmt(r.p95_turn_ttft_ms)} ms</td><td>${fmt(r.p95_session_e2e_ms)} ms</td><td>${pct(r.cache_hit_rate)}</td><td>${pct(r.routing_locality_rate)}</td><td>${fmt(r.recomputed_history_tokens, 0)} tok</td><td>${fmt(r.mean_kv_gb, 3)} GB</td><td>${fmt(r.peak_kv_gb, 3)} GB</td><td>${fmt(r.hbm_gb_seconds, 3)}</td><td>${r.pressure_evictions + r.ttl_evictions}</td></tr>`).join(""); | |
| destroyChart("agentCompare"); | |
| charts.agentCompare = new Chart($("agentCompareChart"), { | |
| type: "scatter", | |
| data: { datasets: result.rows.map((r, i) => ({ label: r.label, data: [{ x: r.mean_kv_gb, y: r.p95_turn_ttft_ms }], backgroundColor: [COLORS.gray, COLORS.amber, COLORS.green, COLORS.blue][i], borderColor: [COLORS.gray, COLORS.amber, COLORS.green, COLORS.blue][i], pointRadius: 7, pointHoverRadius: 9 })) }, | |
| options: { ...commonChartOptions(), plugins: { legend: pointLegend() }, scales: { x: { beginAtZero: true, title: { display: true, text: "Mean resident KV (GB)" } }, y: { beginAtZero: true, title: { display: true, text: "p95 turn TTFT (ms)" } } } }, | |
| }); | |
| } | |
| $("agentCompareBtn").addEventListener("click", async () => { | |
| const button = $("agentCompareBtn"); button.disabled = true; button.textContent = "Comparing..."; | |
| try { renderAgentCompare(await callPython("agent_compare", { config: agentConfigFromUI() })); } | |
| catch (error) { reportError("Agent policy comparison failed", error); } | |
| finally { button.disabled = false; button.textContent = "Compare 4 policies"; } | |
| }); | |
| $("agentCompareCopy").addEventListener("click", () => { if (lastAgentCompare) copyText(tableText(agentCompareHeaders, agentCompareTableRows(lastAgentCompare)), "Agent policy table copied"); }); | |
| $("agentCompareCsv").addEventListener("click", () => { if (lastAgentCompare) downloadCsv(`inferscale_agent-policy-comparison_${stamp()}.csv`, agentCompareHeaders, agentCompareTableRows(lastAgentCompare)); }); | |
| const agentTtlHeaders = ["TTL", "Pareto", "p95 turn TTFT", "p95 session E2E", "Cache hit", "Recomputed history", "Mean KV", "HBM GB-s", "TTL evictions", "Pressure evictions"]; | |
| function agentTtlTableRows(result) { | |
| return result.rows.map((r) => [`${fmt(r.ttl_s, 2)} s`, r.pareto ? "YES" : "NO", `${fmt(r.p95_turn_ttft_ms)} ms`, `${fmt(r.p95_session_e2e_ms)} ms`, pct(r.cache_hit_rate), `${fmt(r.recomputed_history_tokens, 0)} tok`, `${fmt(r.mean_kv_gb, 3)} GB`, fmt(r.hbm_gb_seconds, 3), r.ttl_evictions, r.pressure_evictions]); | |
| } | |
| function renderAgentTtl(result) { | |
| lastAgentTtl = result; | |
| $("agentTtlEmpty").classList.add("hidden"); | |
| $("agentTtlContent").classList.remove("hidden"); | |
| ["agentTtlCopy", "agentTtlCsv"].forEach((id) => { $(id).disabled = false; }); | |
| $("agentTtlRows").innerHTML = result.rows.map((r) => `<tr><td>${fmt(r.ttl_s, 2)} s</td><td class="${r.pareto ? "pass" : ""}">${r.pareto ? "YES" : "NO"}</td><td>${fmt(r.p95_turn_ttft_ms)} ms</td><td>${fmt(r.p95_session_e2e_ms)} ms</td><td>${pct(r.cache_hit_rate)}</td><td>${fmt(r.recomputed_history_tokens, 0)} tok</td><td>${fmt(r.mean_kv_gb, 3)} GB</td><td>${fmt(r.hbm_gb_seconds, 3)}</td><td>${r.ttl_evictions}</td><td>${r.pressure_evictions}</td></tr>`).join(""); | |
| const pareto = result.rows.filter((r) => r.pareto).sort((a, b) => a.mean_kv_gb - b.mean_kv_gb); | |
| destroyChart("agentTtl"); | |
| charts.agentTtl = new Chart($("agentTtlChart"), { | |
| type: "scatter", | |
| data: { datasets: [ | |
| { label: "TTL candidates", data: result.rows.map((r) => ({ x: r.mean_kv_gb, y: r.p95_turn_ttft_ms, ttl: r.ttl_s })), backgroundColor: COLORS.blue, pointRadius: 5, pointHoverRadius: 7 }, | |
| { type: "line", label: "Retention frontier", data: pareto.map((r) => ({ x: r.mean_kv_gb, y: r.p95_turn_ttft_ms })), borderColor: COLORS.amber, backgroundColor: COLORS.amber, pointRadius: 5, tension: 0, fill: false }, | |
| ] }, | |
| options: { ...commonChartOptions(), plugins: { legend: lineLegend(), tooltip: { callbacks: { afterLabel: (ctx) => ctx.datasetIndex === 0 ? `TTL ${fmt(ctx.raw.ttl, 2)} s` : "" } } }, scales: { x: { beginAtZero: true, title: { display: true, text: "Mean resident KV (GB)" } }, y: { beginAtZero: true, title: { display: true, text: "p95 turn TTFT (ms)" } } } }, | |
| }); | |
| } | |
| $("agentTtlBtn").addEventListener("click", async () => { | |
| const button = $("agentTtlBtn"); button.disabled = true; button.textContent = "Sweeping TTL..."; | |
| try { renderAgentTtl(await callPython("agent_ttl_sweep", { config: agentConfigFromUI() })); } | |
| catch (error) { reportError("TTL sweep failed", error); } | |
| finally { button.disabled = false; button.textContent = "Run TTL sweep"; } | |
| }); | |
| $("agentTtlCopy").addEventListener("click", () => { if (lastAgentTtl) copyText(tableText(agentTtlHeaders, agentTtlTableRows(lastAgentTtl)), "TTL sweep copied"); }); | |
| $("agentTtlCsv").addEventListener("click", () => { if (lastAgentTtl) downloadCsv(`inferscale_agent-ttl-frontier_${stamp()}.csv`, agentTtlHeaders, agentTtlTableRows(lastAgentTtl)); }); | |
| const agentMemoryHeaders = ["Policy", "p95 turn TTFT", "p95 session E2E", "Session SLO", "Reuse", "HBM hit", "Host hit", "Mean HBM", "Mean host", "Transfer p95", "Recomputed"]; | |
| function agentMemoryTableRows(result) { | |
| return result.rows.map((r) => [r.label, `${fmt(r.p95_turn_ttft_ms)} ms`, `${fmt(r.p95_session_e2e_ms)} ms`, pct(r.session_slo_attainment), pct(r.cache_hit_rate), pct(r.hbm_hit_rate), pct(r.host_hit_rate), `${fmt(r.mean_hbm_gb, 3)} GB`, `${fmt(r.mean_host_gb, 3)} GB`, `${fmt(r.p95_host_transfer_ms)} ms`, `${fmt(r.recomputed_history_tokens, 0)} tok`]); | |
| } | |
| function renderAgentMemory(result) { | |
| lastAgentMemory = result; | |
| $("agentMemoryEmpty").classList.add("hidden"); | |
| $("agentMemoryContent").classList.remove("hidden"); | |
| $("agentMemoryCompareBlock").classList.remove("hidden"); | |
| ["agentMemoryCopy", "agentMemoryCsv"].forEach((id) => { $(id).disabled = false; }); | |
| $("agentMemoryRows").innerHTML = result.rows.map((r) => `<tr><td>${escapeHtml(r.label)}</td><td>${fmt(r.p95_turn_ttft_ms)} ms</td><td>${fmt(r.p95_session_e2e_ms)} ms</td><td>${pct(r.session_slo_attainment)}</td><td>${pct(r.cache_hit_rate)}</td><td>${pct(r.hbm_hit_rate)}</td><td>${pct(r.host_hit_rate)}</td><td>${fmt(r.mean_hbm_gb, 3)} GB</td><td>${fmt(r.mean_host_gb, 3)} GB</td><td>${fmt(r.p95_host_transfer_ms)} ms</td><td>${fmt(r.recomputed_history_tokens, 0)} tok</td></tr>`).join(""); | |
| const palette = [COLORS.gray, COLORS.amber, COLORS.green, COLORS.blue, COLORS.purple]; | |
| destroyChart("agentMemory"); | |
| charts.agentMemory = new Chart($("agentMemoryChart"), { | |
| type: "scatter", | |
| data: { datasets: result.rows.map((r, i) => ({ label: r.label, data: [{ x: r.mean_hbm_gb, y: r.p95_turn_ttft_ms }], backgroundColor: palette[i], borderColor: palette[i], pointRadius: 7, pointHoverRadius: 9 })) }, | |
| options: { ...commonChartOptions(), plugins: { legend: pointLegend() }, scales: { x: { beginAtZero: true, title: { display: true, text: "Mean HBM KV (GB)" } }, y: { beginAtZero: true, title: { display: true, text: "p95 turn TTFT (ms)" } } } }, | |
| }); | |
| } | |
| $("agentMemoryCompareBtn").addEventListener("click", async () => { | |
| const button = $("agentMemoryCompareBtn"); button.disabled = true; button.textContent = "Comparing policies..."; | |
| try { renderAgentMemory(await callPython("agent_memory_compare", { config: agentConfigFromUI() })); } | |
| catch (error) { reportError("Memory policy comparison failed", error); } | |
| finally { button.disabled = false; button.textContent = "Compare memory policies"; } | |
| }); | |
| $("agentMemoryCopy").addEventListener("click", () => { if (lastAgentMemory) copyText(tableText(agentMemoryHeaders, agentMemoryTableRows(lastAgentMemory)), "Memory-policy table copied"); }); | |
| $("agentMemoryCsv").addEventListener("click", () => { if (lastAgentMemory) downloadCsv(`inferscale_agent-memory-policies_${stamp()}.csv`, agentMemoryHeaders, agentMemoryTableRows(lastAgentMemory)); }); | |
| const agentBudgetHeaders = ["Policy", "Budget / replica", "Reference x", "p95 TTFT", "Session SLO", "Reuse", "Mean HBM", "Mean host", "Pressure evictions", "Failed turns"]; | |
| function agentBudgetTableRows(result) { | |
| return result.rows.map((r) => [r.policy, `${fmt(r.budget_gb_per_replica, 4)} GB`, `${fmt(r.budget_multiplier, 2)}x`, `${fmt(r.p95_turn_ttft_ms)} ms`, pct(r.session_slo_attainment), pct(r.cache_hit_rate), `${fmt(r.mean_hbm_gb, 4)} GB`, `${fmt(r.mean_host_gb, 4)} GB`, r.pressure_evictions, r.turns_failed]); | |
| } | |
| function renderAgentBudget(result) { | |
| lastAgentBudget = result; | |
| $("agentMemoryEmpty").classList.add("hidden"); | |
| $("agentMemoryContent").classList.remove("hidden"); | |
| $("agentBudgetBlock").classList.remove("hidden"); | |
| ["agentBudgetCopy", "agentBudgetCsv"].forEach((id) => { $(id).disabled = false; }); | |
| $("agentBudgetCaption").textContent = `HBM budget sweep (reference peak ${fmt(result.reference_peak_replica_kv_gb, 4)} GB / replica)`; | |
| $("agentBudgetRows").innerHTML = result.rows.map((r) => `<tr><td>${escapeHtml(r.policy)}</td><td>${fmt(r.budget_gb_per_replica, 4)} GB</td><td>${fmt(r.budget_multiplier, 2)}x</td><td>${fmt(r.p95_turn_ttft_ms)} ms</td><td>${pct(r.session_slo_attainment)}</td><td>${pct(r.cache_hit_rate)}</td><td>${fmt(r.mean_hbm_gb, 4)} GB</td><td>${fmt(r.mean_host_gb, 4)} GB</td><td>${r.pressure_evictions}</td><td>${r.turns_failed}</td></tr>`).join(""); | |
| const policies = [...new Set(result.rows.map((r) => r.policy))]; | |
| const palette = [COLORS.green, COLORS.blue, COLORS.purple]; | |
| destroyChart("agentBudget"); | |
| charts.agentBudget = new Chart($("agentBudgetChart"), { | |
| type: "line", | |
| data: { datasets: policies.map((policy, i) => ({ label: policy, data: result.rows.filter((r) => r.policy === policy).sort((a, b) => a.budget_gb_per_replica - b.budget_gb_per_replica).map((r) => ({ x: r.budget_gb_per_replica, y: r.p95_turn_ttft_ms })), borderColor: palette[i], backgroundColor: palette[i], pointRadius: 4, tension: .12, fill: false })) }, | |
| options: { ...commonChartOptions(), parsing: false, plugins: { legend: lineLegend() }, scales: { x: { type: "linear", beginAtZero: true, title: { display: true, text: "HBM KV budget per replica (GB)" } }, y: { beginAtZero: true, title: { display: true, text: "p95 turn TTFT (ms)" } } } }, | |
| }); | |
| } | |
| $("agentBudgetBtn").addEventListener("click", async () => { | |
| const button = $("agentBudgetBtn"); button.disabled = true; button.textContent = "Stressing HBM budget..."; | |
| try { renderAgentBudget(await callPython("agent_memory_sweep", { config: agentConfigFromUI() })); } | |
| catch (error) { reportError("HBM budget study failed", error); } | |
| finally { button.disabled = false; button.textContent = "Stress HBM budget"; } | |
| }); | |
| $("agentBudgetCopy").addEventListener("click", () => { if (lastAgentBudget) copyText(tableText(agentBudgetHeaders, agentBudgetTableRows(lastAgentBudget)), "HBM budget table copied"); }); | |
| $("agentBudgetCsv").addEventListener("click", () => { if (lastAgentBudget) downloadCsv(`inferscale_agent-hbm-budget_${stamp()}.csv`, agentBudgetHeaders, agentBudgetTableRows(lastAgentBudget)); }); | |
| const agentAffinityHeaders = ["Affinity slack", "p95 turn TTFT", "p95 session E2E", "Route locality", "Cache hit", "Recomputed", "Session SLO", "Pressure evictions"]; | |
| function agentAffinityTableRows(result) { | |
| return result.rows.map((r) => [`${fmt(r.affinity_slack_ms, 0)} ms`, `${fmt(r.p95_turn_ttft_ms)} ms`, `${fmt(r.p95_session_e2e_ms)} ms`, pct(r.routing_locality_rate), pct(r.cache_hit_rate), `${fmt(r.recomputed_history_tokens, 0)} tok`, pct(r.session_slo_attainment), r.pressure_evictions]); | |
| } | |
| function renderAgentAffinity(result) { | |
| lastAgentAffinity = result; | |
| $("agentAffinityEmpty").classList.add("hidden"); | |
| $("agentAffinityContent").classList.remove("hidden"); | |
| ["agentAffinityCopy", "agentAffinityCsv"].forEach((id) => { $(id).disabled = false; }); | |
| $("agentAffinityRows").innerHTML = result.rows.map((r) => `<tr><td>${fmt(r.affinity_slack_ms, 0)} ms</td><td>${fmt(r.p95_turn_ttft_ms)} ms</td><td>${fmt(r.p95_session_e2e_ms)} ms</td><td>${pct(r.routing_locality_rate)}</td><td>${pct(r.cache_hit_rate)}</td><td>${fmt(r.recomputed_history_tokens, 0)} tok</td><td>${pct(r.session_slo_attainment)}</td><td>${r.pressure_evictions}</td></tr>`).join(""); | |
| destroyChart("agentAffinity"); | |
| charts.agentAffinity = new Chart($("agentAffinityChart"), { | |
| type: "line", | |
| data: { datasets: [ | |
| { label: "p95 session E2E", data: result.rows.map((r) => ({ x: r.affinity_slack_ms, y: r.p95_session_e2e_ms })), borderColor: COLORS.blue, backgroundColor: COLORS.blue, pointRadius: 4, tension: .12, yAxisID: "yLatency", fill: false }, | |
| { label: "Routing locality", data: result.rows.map((r) => ({ x: r.affinity_slack_ms, y: r.routing_locality_rate * 100 })), borderColor: COLORS.green, backgroundColor: COLORS.green, pointRadius: 4, tension: .12, yAxisID: "yLocality", fill: false }, | |
| ] }, | |
| options: { ...commonChartOptions(), parsing: false, plugins: { legend: lineLegend() }, scales: { x: { type: "linear", beginAtZero: true, title: { display: true, text: "Affinity slack (ms)" } }, yLatency: { position: "left", beginAtZero: true, title: { display: true, text: "p95 session E2E (ms)" } }, yLocality: { position: "right", min: 0, max: 100, grid: { drawOnChartArea: false }, title: { display: true, text: "Routing locality (%)" } } } }, | |
| }); | |
| } | |
| $("agentAffinityBtn").addEventListener("click", async () => { | |
| const button = $("agentAffinityBtn"); button.disabled = true; button.textContent = "Sweeping affinity..."; | |
| try { renderAgentAffinity(await callPython("agent_affinity_sweep", { config: agentConfigFromUI() })); } | |
| catch (error) { reportError("Affinity sweep failed", error); } | |
| finally { button.disabled = false; button.textContent = "Run affinity sweep"; } | |
| }); | |
| $("agentAffinityCopy").addEventListener("click", () => { if (lastAgentAffinity) copyText(tableText(agentAffinityHeaders, agentAffinityTableRows(lastAgentAffinity)), "Affinity sweep copied"); }); | |
| $("agentAffinityCsv").addEventListener("click", () => { if (lastAgentAffinity) downloadCsv(`inferscale_agent-affinity-frontier_${stamp()}.csv`, agentAffinityHeaders, agentAffinityTableRows(lastAgentAffinity)); }); | |
| const predictiveHeaders = ["Policy", "p95 turn TTFT", "p95 session E2E", "Session SLO", "Reuse", "Mean HBM", "Mean host", "Prediction MAE", "Oracle agreement", "Post-shift MAE", "Recomputed"]; | |
| function predictiveTableRows(result) { | |
| return result.rows.map((r) => [ | |
| r.label, | |
| `${fmt(r.p95_turn_ttft_ms)} ms`, | |
| `${fmt(r.p95_session_e2e_ms)} ms`, | |
| pct(r.session_slo_attainment), | |
| pct(r.cache_hit_rate), | |
| `${fmt(r.mean_hbm_gb, 4)} GB`, | |
| `${fmt(r.mean_host_gb, 4)} GB`, | |
| r.prediction_count ? `${fmt(r.prediction_mae_s, 3)} s` : "N/A", | |
| r.prediction_count ? pct(r.oracle_action_agreement) : "N/A", | |
| r.prediction_count ? `${fmt(r.post_shift_mae_s, 3)} s` : "N/A", | |
| `${fmt(r.recomputed_history_tokens, 0)} tok`, | |
| ]); | |
| } | |
| function renderPredictiveStudy(result) { | |
| lastPredictiveStudy = result; | |
| $("predictiveEmpty").classList.add("hidden"); | |
| $("predictiveContent").classList.remove("hidden"); | |
| $("predictiveCompareBlock").classList.remove("hidden"); | |
| ["predictiveCopy", "predictiveCsv", "predictiveJson"].forEach((id) => { $(id).disabled = false; }); | |
| const nonOracle = result.rows.filter((r) => r.retention !== "gap_aware"); | |
| const best = [...nonOracle].sort((a, b) => a.p95_turn_ttft_ms - b.p95_turn_ttft_ms)[0]; | |
| const tool = result.rows.find((r) => r.label === "Adaptive per-tool EWMA"); | |
| $("predictiveBestTtft").textContent = best ? `${fmt(best.p95_turn_ttft_ms)} ms` : "N/A"; | |
| $("predictiveAgreement").textContent = tool ? pct(tool.oracle_action_agreement) : "N/A"; | |
| $("predictivePostMae").textContent = tool ? `${fmt(tool.post_shift_mae_s, 3)} s` : "N/A"; | |
| $("predictiveShiftValue").textContent = `${fmt(result.shift_fraction * 100, 0)}% / ${fmt(result.shift_multiplier, 2)}x`; | |
| $("predictiveRows").innerHTML = result.rows.map((r) => `<tr><td>${escapeHtml(r.label)}</td><td>${fmt(r.p95_turn_ttft_ms)} ms</td><td>${fmt(r.p95_session_e2e_ms)} ms</td><td>${pct(r.session_slo_attainment)}</td><td>${pct(r.cache_hit_rate)}</td><td>${fmt(r.mean_hbm_gb, 4)} GB</td><td>${fmt(r.mean_host_gb, 4)} GB</td><td>${r.prediction_count ? `${fmt(r.prediction_mae_s, 3)} s` : "N/A"}</td><td>${r.prediction_count ? pct(r.oracle_action_agreement) : "N/A"}</td><td>${r.prediction_count ? `${fmt(r.post_shift_mae_s, 3)} s` : "N/A"}</td><td>${fmt(r.recomputed_history_tokens, 0)} tok</td></tr>`).join(""); | |
| const palette = [COLORS.amber, COLORS.steel, COLORS.purple, COLORS.blue, COLORS.green]; | |
| destroyChart("predictivePolicy"); | |
| charts.predictivePolicy = new Chart($("predictivePolicyChart"), { | |
| type: "scatter", | |
| data: { datasets: result.rows.map((r, i) => ({ label: r.label, data: [{ x: r.mean_hbm_gb, y: r.p95_turn_ttft_ms }], backgroundColor: palette[i], borderColor: palette[i], pointRadius: 7, pointHoverRadius: 9 })) }, | |
| options: { ...commonChartOptions(), parsing: false, scales: { x: { type: "linear", beginAtZero: true, title: { display: true, text: "Mean HBM KV (GB)" } }, y: { beginAtZero: true, title: { display: true, text: "p95 turn TTFT (ms)" } } } }, | |
| }); | |
| const globalCurve = result.learning_curves.global || []; | |
| const toolCurve = result.learning_curves.per_tool || []; | |
| const maxMae = Math.max(0.1, ...globalCurve.map((r) => r.rolling_mae_s), ...toolCurve.map((r) => r.rolling_mae_s)); | |
| const shiftX = result.shift_observation || 0; | |
| const learningDatasets = [ | |
| { label: "Global EWMA rolling MAE", data: globalCurve.map((r) => ({ x: r.observation, y: r.rolling_mae_s })), borderColor: COLORS.steel, backgroundColor: COLORS.steel, pointRadius: 0, tension: .16, fill: false }, | |
| { label: "Per-tool EWMA rolling MAE", data: toolCurve.map((r) => ({ x: r.observation, y: r.rolling_mae_s })), borderColor: COLORS.blue, backgroundColor: COLORS.blue, pointRadius: 0, tension: .16, fill: false }, | |
| ]; | |
| if (shiftX > 0) learningDatasets.push({ label: "Regime shift", data: [{ x: shiftX, y: 0 }, { x: shiftX, y: maxMae }], borderColor: COLORS.amber, backgroundColor: COLORS.amber, pointRadius: 0, borderDash: [6, 5], fill: false }); | |
| destroyChart("predictiveLearning"); | |
| charts.predictiveLearning = new Chart($("predictiveLearningChart"), { | |
| type: "line", | |
| data: { datasets: learningDatasets }, | |
| options: { ...commonChartOptions(), parsing: false, plugins: { legend: lineLegend() }, scales: { x: { type: "linear", beginAtZero: true, title: { display: true, text: "Completed tool observations" } }, y: { beginAtZero: true, title: { display: true, text: "Rolling absolute error (s)" } } } }, | |
| }); | |
| } | |
| function predictivePayload() { | |
| return { | |
| config: agentConfigFromUI({ routing_policy: "bounded_affinity" }), | |
| horizon_s: num("predictiveHorizon"), | |
| shift_fraction: num("predictiveShift"), | |
| shift_multiplier: num("predictiveMultiplier"), | |
| alpha: num("predictiveAlpha"), | |
| }; | |
| } | |
| $("predictiveCompareBtn").addEventListener("click", async () => { | |
| const button = $("predictiveCompareBtn"); button.disabled = true; button.textContent = "Running predictive study..."; | |
| try { renderPredictiveStudy(await callPython("agent_predictive_tiering", predictivePayload())); } | |
| catch (error) { reportError("Predictive tiering study failed", error); } | |
| finally { button.disabled = false; button.textContent = "Compare predictive policies"; } | |
| }); | |
| $("predictiveCopy").addEventListener("click", () => { if (lastPredictiveStudy) copyText(tableText(predictiveHeaders, predictiveTableRows(lastPredictiveStudy)), "Predictive-tiering table copied"); }); | |
| $("predictiveCsv").addEventListener("click", () => { if (lastPredictiveStudy) downloadCsv(`inferscale_predictive-tiering_${stamp()}.csv`, predictiveHeaders, predictiveTableRows(lastPredictiveStudy)); }); | |
| $("predictiveJson").addEventListener("click", () => { if (lastPredictiveStudy) downloadNamedJson("inferscale_predictive-tiering", lastPredictiveStudy); }); | |
| const predictiveAlphaHeaders = ["Alpha", "Pre-shift MAE", "Post-shift MAE", "Oracle agreement", "p95 TTFT", "p95 session E2E", "Session SLO", "Mean HBM", "Mean host"]; | |
| function predictiveAlphaTableRows(result) { | |
| return result.rows.map((r) => [fmt(r.alpha, 2), `${fmt(r.pre_shift_mae_s, 3)} s`, `${fmt(r.post_shift_mae_s, 3)} s`, pct(r.oracle_action_agreement), `${fmt(r.p95_turn_ttft_ms)} ms`, `${fmt(r.p95_session_e2e_ms)} ms`, pct(r.session_slo_attainment), `${fmt(r.mean_hbm_gb, 4)} GB`, `${fmt(r.mean_host_gb, 4)} GB`]); | |
| } | |
| function renderPredictiveAlpha(result) { | |
| lastPredictiveAlpha = result; | |
| $("predictiveEmpty").classList.add("hidden"); | |
| $("predictiveContent").classList.remove("hidden"); | |
| $("predictiveAlphaBlock").classList.remove("hidden"); | |
| ["predictiveAlphaCopy", "predictiveAlphaCsv", "predictiveAlphaJson"].forEach((id) => { $(id).disabled = false; }); | |
| $("predictiveAlphaCaption").textContent = `Adaptation-rate sweep (best post-shift alpha ${fmt(result.best_post_shift_alpha, 2)})`; | |
| $("predictiveAlphaRows").innerHTML = result.rows.map((r) => `<tr><td>${fmt(r.alpha, 2)}</td><td>${fmt(r.pre_shift_mae_s, 3)} s</td><td>${fmt(r.post_shift_mae_s, 3)} s</td><td>${pct(r.oracle_action_agreement)}</td><td>${fmt(r.p95_turn_ttft_ms)} ms</td><td>${fmt(r.p95_session_e2e_ms)} ms</td><td>${pct(r.session_slo_attainment)}</td><td>${fmt(r.mean_hbm_gb, 4)} GB</td><td>${fmt(r.mean_host_gb, 4)} GB</td></tr>`).join(""); | |
| destroyChart("predictiveAlpha"); | |
| charts.predictiveAlpha = new Chart($("predictiveAlphaChart"), { | |
| type: "line", | |
| data: { datasets: [ | |
| { label: "Pre-shift MAE", data: result.rows.map((r) => ({ x: r.alpha, y: r.pre_shift_mae_s })), borderColor: COLORS.steel, backgroundColor: COLORS.steel, pointRadius: 4, tension: .12, fill: false }, | |
| { label: "Post-shift MAE", data: result.rows.map((r) => ({ x: r.alpha, y: r.post_shift_mae_s })), borderColor: COLORS.blue, backgroundColor: COLORS.blue, pointRadius: 4, tension: .12, fill: false }, | |
| ] }, | |
| options: { ...commonChartOptions(), parsing: false, plugins: { legend: lineLegend() }, scales: { x: { type: "linear", min: 0, max: 1, title: { display: true, text: "EWMA alpha" } }, y: { beginAtZero: true, title: { display: true, text: "Mean absolute error (s)" } } } }, | |
| }); | |
| } | |
| $("predictiveAlphaBtn").addEventListener("click", async () => { | |
| const button = $("predictiveAlphaBtn"); button.disabled = true; button.textContent = "Sweeping adaptation rate..."; | |
| const payload = predictivePayload(); | |
| try { renderPredictiveAlpha(await callPython("agent_adaptive_alpha_sweep", payload)); } | |
| catch (error) { reportError("Adaptation-rate sweep failed", error); } | |
| finally { button.disabled = false; button.textContent = "Sweep adaptation rate"; } | |
| }); | |
| $("predictiveAlphaCopy").addEventListener("click", () => { if (lastPredictiveAlpha) copyText(tableText(predictiveAlphaHeaders, predictiveAlphaTableRows(lastPredictiveAlpha)), "Adaptation-rate table copied"); }); | |
| $("predictiveAlphaCsv").addEventListener("click", () => { if (lastPredictiveAlpha) downloadCsv(`inferscale_adaptation-rate-sweep_${stamp()}.csv`, predictiveAlphaHeaders, predictiveAlphaTableRows(lastPredictiveAlpha)); }); | |
| $("predictiveAlphaJson").addEventListener("click", () => { if (lastPredictiveAlpha) downloadNamedJson("inferscale_adaptation-rate-sweep", lastPredictiveAlpha); }); | |
| function executionConfigFromUI(overrides = {}) { | |
| return { | |
| model: $("execModel").value, | |
| accelerator: $("execAccelerator").value, | |
| quantization: $("execQuantization").value, | |
| seed: num("execSeed"), | |
| duration_s: num("execDuration"), | |
| workflow_rate_rps: num("execRate"), | |
| max_steps: num("execMaxSteps"), | |
| shift_fraction: num("execShift"), | |
| dynamic_prompt_tokens_mean: num("execPrompt"), | |
| output_tokens_mean: num("execOutput"), | |
| tool_gap_mean_s: num("execGap"), | |
| prefix_cache_budget_fraction: num("execCacheBudget"), | |
| prefetch_policy: $("execPolicy").value, | |
| transition_decay: num("execDecay"), | |
| confidence_threshold: num("execThreshold"), | |
| host_bandwidth_gbps: num("execBandwidth"), | |
| forecast_horizon: num("execHorizon"), | |
| prefetch_top_k: num("execTopK"), | |
| forecast_discount: num("execDiscount"), | |
| forecast_min_score: num("execMinScore"), | |
| utility_threshold_ms: num("execUtility"), | |
| ...overrides, | |
| }; | |
| } | |
| function rollingBinaryAccuracy(rows, windowSize = 16) { | |
| const width = Math.max(4, windowSize); | |
| return (rows || []).map((row, idx) => { | |
| const chunk = rows.slice(Math.max(0, idx - width + 1), idx + 1); | |
| const correct = chunk.filter((item) => item.prediction_correct).length; | |
| return { x: idx + 1, y: chunk.length ? (correct / chunk.length) * 100 : 0, postShift: Boolean(row.post_shift) }; | |
| }); | |
| } | |
| function renderExecutionRun(result) { | |
| lastExecutionRun = result; | |
| $("execRunEmpty").classList.add("hidden"); | |
| $("execRunContent").classList.remove("hidden"); | |
| ["execRunCopyJson", "execRunJson"].forEach((id) => { $(id).disabled = false; }); | |
| const l = result.latency; | |
| const r = result.resource; | |
| const p = result.prediction; | |
| const s = result.summary; | |
| $("execTtft").textContent = `${fmt(l.step_ttft_ms.p95)} ms`; | |
| $("execE2e").textContent = `${fmt(l.workflow_e2e_ms.p95)} ms`; | |
| $("execAccuracy").textContent = pct(p.top1_accuracy); | |
| $("execPostAccuracy").textContent = pct(p.post_shift_accuracy); | |
| $("execPrefixHit").textContent = pct(r.prefix_hit_rate); | |
| $("execPrecision").textContent = r.prefetch_attempts ? pct(r.prefetch_precision) : "N/A"; | |
| $("execCoverage").textContent = pct(r.prefetch_coverage); | |
| $("execWaste").textContent = `${fmt(r.wrong_step_prefetch_gb, 4)} GB`; | |
| $("execEce").textContent = pct(p.calibration?.ece || 0); | |
| $("execForecastRecall").textContent = pct(r.forecast_recall || 0); | |
| $("execUtilization").textContent = r.prefetch_attempts ? pct(r.prefetch_utilization || 0) : "N/A"; | |
| $("execUnused").textContent = `${fmt(r.unused_prefetch_gb || 0, 4)} GB`; | |
| $("execRunState").textContent = `${s.workflows_completed}/${s.workflows_generated} workflows`; | |
| $("execRunState").className = `tag ${s.workflow_completion_rate === 1 ? "good" : "bad"}`; | |
| const lookahead = result.provenance.lookahead === "oracle-upper-bound" ? "oracle upper bound" : "online/no-lookahead"; | |
| $("execRunSummary").innerHTML = `<strong>${escapeHtml(result.config.prefetch_policy.replaceAll("_", " "))} policy.</strong> ${p.count} observed transitions; top-1 accuracy ${pct(p.top1_accuracy)} overall and ${pct(p.post_shift_accuracy)} after the workflow shift. Calibration ECE is ${pct(p.calibration?.ece || 0)}. Forecast-set recall is ${pct(r.forecast_recall || 0)}; ${r.prefetch_attempts || 0} prefetch attempts achieved ${pct(r.prefetch_utilization || 0)} eventual utilization and left ${fmt(r.unused_prefetch_gb || 0, 4)} GB unused before eviction/end. Provenance: ${escapeHtml(lookahead)}.`; | |
| const timeline = result.timeline || []; | |
| destroyChart("execTimeline"); | |
| charts.execTimeline = new Chart($("execTimelineChart"), { | |
| type: "line", | |
| data: { datasets: [ | |
| { label: "Prefix HBM GB", data: timeline.map((x) => ({ x: x.time_s, y: x.prefix_hbm_gb })), borderColor: COLORS.green, backgroundColor: COLORS.green, pointRadius: 0, tension: .08, yAxisID: "yKv" }, | |
| { label: "Queued steps", data: timeline.map((x) => ({ x: x.time_s, y: x.queued_steps })), borderColor: COLORS.amber, backgroundColor: COLORS.amber, pointRadius: 0, tension: .08, yAxisID: "yCount" }, | |
| { label: "Transfer busy", data: timeline.map((x) => ({ x: x.time_s, y: x.transfer_busy })), borderColor: COLORS.steel, backgroundColor: COLORS.steel, pointRadius: 0, borderDash: [6, 4], tension: 0, yAxisID: "yCount" }, | |
| ] }, | |
| options: { ...commonChartOptions(), parsing: false, plugins: { legend: lineLegend() }, scales: { x: { type: "linear", title: { display: true, text: "Virtual time (s)" } }, yCount: { position: "left", beginAtZero: true, title: { display: true, text: "Queued steps / transfer busy" } }, yKv: { position: "right", beginAtZero: true, grid: { drawOnChartArea: false }, title: { display: true, text: "Prefix HBM (GB)" } } } }, | |
| }); | |
| const curve = rollingBinaryAccuracy(p.rows || []); | |
| const shiftPoint = curve.find((point) => point.postShift)?.x || 0; | |
| const datasets = [ | |
| { label: "Rolling top-1 accuracy", data: curve.map((point) => ({ x: point.x, y: point.y })), borderColor: COLORS.blue, backgroundColor: COLORS.blue, pointRadius: 0, tension: .15, fill: false }, | |
| ]; | |
| if (shiftPoint > 0) datasets.push({ label: "Workflow shift", data: [{ x: shiftPoint, y: 0 }, { x: shiftPoint, y: 100 }], borderColor: COLORS.amber, backgroundColor: COLORS.amber, pointRadius: 0, borderDash: [6, 5], fill: false }); | |
| destroyChart("execLearning"); | |
| charts.execLearning = new Chart($("execLearningChart"), { | |
| type: "line", | |
| data: { datasets }, | |
| options: { ...commonChartOptions(), parsing: false, plugins: { legend: lineLegend() }, scales: { x: { type: "linear", beginAtZero: true, title: { display: true, text: "Observed transitions" } }, y: { min: 0, max: 100, title: { display: true, text: "Rolling top-1 accuracy (%)" } } } }, | |
| }); | |
| const calibrationBins = p.calibration?.bins || []; | |
| destroyChart("execCalibration"); | |
| charts.execCalibration = new Chart($("execCalibrationChart"), { | |
| type: "line", | |
| data: { datasets: [ | |
| { label: "Observed accuracy", data: calibrationBins.map((b) => ({ x: b.mean_confidence * 100, y: b.accuracy * 100 })), borderColor: COLORS.blue, backgroundColor: COLORS.blue, pointRadius: 5, tension: .08, fill: false }, | |
| { label: "Perfect calibration", data: [{ x: 0, y: 0 }, { x: 100, y: 100 }], borderColor: COLORS.steel, backgroundColor: COLORS.steel, borderDash: [6, 5], pointRadius: 0, fill: false }, | |
| ] }, | |
| options: { ...commonChartOptions(), parsing: false, plugins: { legend: lineLegend() }, scales: { x: { type: "linear", min: 0, max: 100, title: { display: true, text: "Mean predicted confidence (%)" } }, y: { min: 0, max: 100, title: { display: true, text: "Observed top-1 accuracy (%)" } } } }, | |
| }); | |
| } | |
| $("execRunBtn").addEventListener("click", async () => { | |
| const button = $("execRunBtn"); | |
| button.disabled = true; button.textContent = "Running execution model..."; | |
| $("execRunState").textContent = "Running..."; $("execRunState").className = "tag neutral"; | |
| try { renderExecutionRun(await callPython("execution_learning_run", { config: executionConfigFromUI() })); } | |
| catch (error) { $("execRunState").textContent = "Error"; $("execRunState").className = "tag bad"; reportError("Execution-learning run failed", error); } | |
| finally { button.disabled = false; button.textContent = "Run execution-learning simulation"; } | |
| }); | |
| $("execRunCopyJson").addEventListener("click", () => { if (lastExecutionRun) copyText(JSON.stringify(lastExecutionRun, null, 2), "Execution-learning JSON copied"); }); | |
| $("execRunJson").addEventListener("click", () => { if (lastExecutionRun) downloadNamedJson("inferscale_execution-learning-run", lastExecutionRun); }); | |
| const execCompareHeaders = ["Policy", "p95 TTFT", "p95 workflow E2E", "Post-shift top-1", "Prefix hit", "Prefetch precision", "Coverage", "Mean HBM", "Wrong prefetch", "Saved prefill"]; | |
| function execCompareTableRows(result) { | |
| return result.rows.map((r) => [r.label, `${fmt(r.p95_step_ttft_ms)} ms`, `${fmt(r.p95_workflow_e2e_ms)} ms`, pct(r.post_shift_accuracy), pct(r.prefix_hit_rate), r.prefetch_coverage ? pct(r.prefetch_precision) : "N/A", pct(r.prefetch_coverage), `${fmt(r.mean_hbm_gb, 4)} GB`, `${fmt(r.wrong_step_prefetch_gb, 4)} GB`, `${fmt(r.prefill_tokens_saved, 0)} tok`]); | |
| } | |
| function renderExecutionCompare(result) { | |
| lastExecutionCompare = result; | |
| $("execCompareEmpty").classList.add("hidden"); $("execCompareContent").classList.remove("hidden"); | |
| ["execCompareCopy", "execCompareCsv", "execCompareJson"].forEach((id) => { $(id).disabled = false; }); | |
| $("execCompareRows").innerHTML = result.rows.map((r) => `<tr><td>${escapeHtml(r.label)}</td><td>${fmt(r.p95_step_ttft_ms)} ms</td><td>${fmt(r.p95_workflow_e2e_ms)} ms</td><td>${pct(r.post_shift_accuracy)}</td><td>${pct(r.prefix_hit_rate)}</td><td>${r.prefetch_coverage ? pct(r.prefetch_precision) : "N/A"}</td><td>${pct(r.prefetch_coverage)}</td><td>${fmt(r.mean_hbm_gb, 4)} GB</td><td>${fmt(r.wrong_step_prefetch_gb, 4)} GB</td><td>${fmt(r.prefill_tokens_saved, 0)} tok</td></tr>`).join(""); | |
| const palette = [COLORS.gray, COLORS.amber, COLORS.blue, COLORS.green]; | |
| destroyChart("execCompare"); | |
| charts.execCompare = new Chart($("execCompareChart"), { | |
| type: "scatter", | |
| data: { datasets: result.rows.map((r, i) => ({ label: r.label, data: [{ x: r.post_shift_accuracy * 100, y: r.p95_step_ttft_ms }], backgroundColor: palette[i], borderColor: palette[i], pointRadius: 7, pointHoverRadius: 9 })) }, | |
| options: { ...commonChartOptions(), plugins: { legend: pointLegend() }, scales: { x: { min: 0, max: 100, title: { display: true, text: "Post-shift next-role top-1 accuracy (%)" } }, y: { beginAtZero: true, title: { display: true, text: "p95 step TTFT (ms)" } } } }, | |
| }); | |
| } | |
| $("execCompareBtn").addEventListener("click", async () => { | |
| const button = $("execCompareBtn"); button.disabled = true; button.textContent = "Comparing policies..."; | |
| try { renderExecutionCompare(await callPython("execution_prefetch_study", { config: executionConfigFromUI() })); } | |
| catch (error) { reportError("Execution prefetch study failed", error); } | |
| finally { button.disabled = false; button.textContent = "Compare 4 policies"; } | |
| }); | |
| $("execCompareCopy").addEventListener("click", () => { if (lastExecutionCompare) copyText(tableText(execCompareHeaders, execCompareTableRows(lastExecutionCompare)), "Execution policy table copied"); }); | |
| $("execCompareCsv").addEventListener("click", () => { if (lastExecutionCompare) downloadCsv(`inferscale_execution-prefetch-study_${stamp()}.csv`, execCompareHeaders, execCompareTableRows(lastExecutionCompare)); }); | |
| $("execCompareJson").addEventListener("click", () => { if (lastExecutionCompare) downloadNamedJson("inferscale_execution-prefetch-study", lastExecutionCompare); }); | |
| const execThresholdHeaders = ["Threshold", "p95 TTFT", "Post-shift top-1", "Precision", "Coverage", "Prefix hit", "Wrong prefetch", "Mean HBM"]; | |
| function execThresholdTableRows(result) { | |
| return result.rows.map((r) => [fmt(r.threshold, 2), `${fmt(r.p95_step_ttft_ms)} ms`, pct(r.post_shift_accuracy), r.prefetch_coverage ? pct(r.prefetch_precision) : "N/A", pct(r.prefetch_coverage), pct(r.prefix_hit_rate), `${fmt(r.wrong_step_prefetch_gb, 4)} GB`, `${fmt(r.mean_hbm_gb, 4)} GB`]); | |
| } | |
| function renderExecutionThreshold(result) { | |
| lastExecutionThreshold = result; | |
| $("execThresholdEmpty").classList.add("hidden"); $("execThresholdContent").classList.remove("hidden"); | |
| ["execThresholdCopy", "execThresholdCsv", "execThresholdJson"].forEach((id) => { $(id).disabled = false; }); | |
| $("execCoverageCorr").textContent = fmt(result.association.coverage_vs_ttft_r, 3); | |
| $("execPrecisionCorr").textContent = fmt(result.association.precision_vs_wrong_prefetch_r, 3); | |
| const bestTtft = result.rows.reduce((a, b) => a.p95_step_ttft_ms <= b.p95_step_ttft_ms ? a : b); | |
| const bestWaste = result.rows.reduce((a, b) => a.wrong_step_prefetch_gb <= b.wrong_step_prefetch_gb ? a : b); | |
| $("execThresholdBestTtft").textContent = `${fmt(bestTtft.threshold, 2)} / ${fmt(bestTtft.p95_step_ttft_ms)} ms`; | |
| $("execThresholdBestWaste").textContent = `${fmt(bestWaste.threshold, 2)} / ${fmt(bestWaste.wrong_step_prefetch_gb, 4)} GB`; | |
| $("execThresholdRows").innerHTML = result.rows.map((r) => `<tr><td>${fmt(r.threshold, 2)}</td><td>${fmt(r.p95_step_ttft_ms)} ms</td><td>${pct(r.post_shift_accuracy)}</td><td>${r.prefetch_coverage ? pct(r.prefetch_precision) : "N/A"}</td><td>${pct(r.prefetch_coverage)}</td><td>${pct(r.prefix_hit_rate)}</td><td>${fmt(r.wrong_step_prefetch_gb, 4)} GB</td><td>${fmt(r.mean_hbm_gb, 4)} GB</td></tr>`).join(""); | |
| destroyChart("execThreshold"); | |
| charts.execThreshold = new Chart($("execThresholdChart"), { | |
| type: "line", | |
| data: { datasets: [ | |
| { label: "p95 step TTFT", data: result.rows.map((r) => ({ x: r.threshold, y: r.p95_step_ttft_ms })), borderColor: COLORS.blue, backgroundColor: COLORS.blue, pointRadius: 4, tension: .1, yAxisID: "yLatency", fill: false }, | |
| { label: "Wrong-step prefetch GB", data: result.rows.map((r) => ({ x: r.threshold, y: r.wrong_step_prefetch_gb })), borderColor: COLORS.amber, backgroundColor: COLORS.amber, pointRadius: 4, tension: .1, yAxisID: "yWaste", fill: false }, | |
| ] }, | |
| options: { ...commonChartOptions(), parsing: false, plugins: { legend: lineLegend() }, scales: { x: { type: "linear", min: 0, max: 1, title: { display: true, text: "Prefetch confidence threshold" } }, yLatency: { position: "left", beginAtZero: true, title: { display: true, text: "p95 TTFT (ms)" } }, yWaste: { position: "right", beginAtZero: true, grid: { drawOnChartArea: false }, title: { display: true, text: "Wrong-step prefetch (GB)" } } } }, | |
| }); | |
| } | |
| $("execThresholdBtn").addEventListener("click", async () => { | |
| const button = $("execThresholdBtn"); button.disabled = true; button.textContent = "Sweeping threshold..."; | |
| try { renderExecutionThreshold(await callPython("execution_threshold_sweep", { config: executionConfigFromUI() })); } | |
| catch (error) { reportError("Execution threshold sweep failed", error); } | |
| finally { button.disabled = false; button.textContent = "Sweep confidence threshold"; } | |
| }); | |
| $("execThresholdCopy").addEventListener("click", () => { if (lastExecutionThreshold) copyText(tableText(execThresholdHeaders, execThresholdTableRows(lastExecutionThreshold)), "Execution threshold table copied"); }); | |
| $("execThresholdCsv").addEventListener("click", () => { if (lastExecutionThreshold) downloadCsv(`inferscale_execution-threshold-sweep_${stamp()}.csv`, execThresholdHeaders, execThresholdTableRows(lastExecutionThreshold)); }); | |
| $("execThresholdJson").addEventListener("click", () => { if (lastExecutionThreshold) downloadNamedJson("inferscale_execution-threshold-sweep", lastExecutionThreshold); }); | |
| const execDecayHeaders = ["Decay", "Pre-shift top-1", "Post-shift top-1", "p95 TTFT", "Prefix hit", "Precision", "Coverage", "Wrong prefetch"]; | |
| function execDecayTableRows(result) { | |
| return result.rows.map((r) => [fmt(r.decay, 2), pct(r.pre_shift_accuracy), pct(r.post_shift_accuracy), `${fmt(r.p95_step_ttft_ms)} ms`, pct(r.prefix_hit_rate), r.prefetch_coverage ? pct(r.prefetch_precision) : "N/A", pct(r.prefetch_coverage), `${fmt(r.wrong_step_prefetch_gb, 4)} GB`]); | |
| } | |
| function renderExecutionDecay(result) { | |
| lastExecutionDecay = result; | |
| $("execDecayEmpty").classList.add("hidden"); $("execDecayContent").classList.remove("hidden"); | |
| ["execDecayCopy", "execDecayCsv", "execDecayJson"].forEach((id) => { $(id).disabled = false; }); | |
| $("execBestAccuracyDecay").textContent = fmt(result.best_post_shift_accuracy_decay, 2); | |
| $("execBestTtftDecay").textContent = fmt(result.best_ttft_decay, 2); | |
| $("execAccuracyTtftCorr").textContent = fmt(result.association.post_shift_accuracy_vs_ttft_r, 3); | |
| $("execAccuracyHitCorr").textContent = fmt(result.association.post_shift_accuracy_vs_prefix_hit_r, 3); | |
| $("execDecayRows").innerHTML = result.rows.map((r) => `<tr><td>${fmt(r.decay, 2)}</td><td>${pct(r.pre_shift_accuracy)}</td><td>${pct(r.post_shift_accuracy)}</td><td>${fmt(r.p95_step_ttft_ms)} ms</td><td>${pct(r.prefix_hit_rate)}</td><td>${r.prefetch_coverage ? pct(r.prefetch_precision) : "N/A"}</td><td>${pct(r.prefetch_coverage)}</td><td>${fmt(r.wrong_step_prefetch_gb, 4)} GB</td></tr>`).join(""); | |
| destroyChart("execDecay"); | |
| charts.execDecay = new Chart($("execDecayChart"), { | |
| type: "line", | |
| data: { datasets: [ | |
| { label: "Post-shift top-1 accuracy", data: result.rows.map((r) => ({ x: r.decay, y: r.post_shift_accuracy * 100 })), borderColor: COLORS.green, backgroundColor: COLORS.green, pointRadius: 4, tension: .1, yAxisID: "yAccuracy", fill: false }, | |
| { label: "p95 step TTFT", data: result.rows.map((r) => ({ x: r.decay, y: r.p95_step_ttft_ms })), borderColor: COLORS.blue, backgroundColor: COLORS.blue, pointRadius: 4, tension: .1, yAxisID: "yLatency", fill: false }, | |
| ] }, | |
| options: { ...commonChartOptions(), parsing: false, plugins: { legend: lineLegend() }, scales: { x: { type: "linear", min: .2, max: 1, title: { display: true, text: "Transition retention / decay" } }, yAccuracy: { position: "left", min: 0, max: 100, title: { display: true, text: "Post-shift top-1 accuracy (%)" } }, yLatency: { position: "right", beginAtZero: true, grid: { drawOnChartArea: false }, title: { display: true, text: "p95 TTFT (ms)" } } } }, | |
| }); | |
| } | |
| $("execDecayBtn").addEventListener("click", async () => { | |
| const button = $("execDecayBtn"); button.disabled = true; button.textContent = "Sweeping decay..."; | |
| try { renderExecutionDecay(await callPython("execution_decay_sweep", { config: executionConfigFromUI() })); } | |
| catch (error) { reportError("Execution decay sweep failed", error); } | |
| finally { button.disabled = false; button.textContent = "Sweep transition decay"; } | |
| }); | |
| $("execDecayCopy").addEventListener("click", () => { if (lastExecutionDecay) copyText(tableText(execDecayHeaders, execDecayTableRows(lastExecutionDecay)), "Execution decay table copied"); }); | |
| $("execDecayCsv").addEventListener("click", () => { if (lastExecutionDecay) downloadCsv(`inferscale_execution-decay-sweep_${stamp()}.csv`, execDecayHeaders, execDecayTableRows(lastExecutionDecay)); }); | |
| $("execDecayJson").addEventListener("click", () => { if (lastExecutionDecay) downloadNamedJson("inferscale_execution-decay-sweep", lastExecutionDecay); }); | |
| const execPlanningHeaders = ["Policy", "p95 TTFT", "Future-role recall@K", "Prefix hit", "Utilization", "Next-step precision", "Mean HBM", "Unused prefetch", "Saved prefill", "ECE"]; | |
| function execPlanningTableRows(result) { | |
| return result.rows.map((r) => [ | |
| r.label, `${fmt(r.p95_step_ttft_ms)} ms`, pct(r.forecast_recall), pct(r.prefix_hit_rate), | |
| r.prefetch_coverage ? pct(r.prefetch_utilization) : "N/A", r.prefetch_coverage ? pct(r.prefetch_precision) : "N/A", | |
| `${fmt(r.mean_hbm_gb, 4)} GB`, `${fmt(r.unused_prefetch_gb, 4)} GB`, `${fmt(r.prefill_tokens_saved, 0)} tok`, pct(r.ece), | |
| ]); | |
| } | |
| function renderExecutionPlanning(result) { | |
| lastExecutionPlanning = result; | |
| $("execPlanningEmpty").classList.add("hidden"); $("execPlanningContent").classList.remove("hidden"); | |
| ["execPlanningCopy", "execPlanningCsv", "execPlanningJson"].forEach((id) => { $(id).disabled = false; }); | |
| $("execPlanningBestTtft").textContent = result.best_ttft_policy || "N/A"; | |
| $("execPlanningBestEfficiency").textContent = result.best_prefill_per_hbm_policy || "N/A"; | |
| $("execPlanningHorizon").textContent = `${num("execHorizon")} steps`; | |
| $("execPlanningTopK").textContent = `${num("execTopK")} roles`; | |
| $("execPlanningRows").innerHTML = execPlanningTableRows(result).map((row) => `<tr>${row.map((cell) => `<td>${escapeHtml(String(cell))}</td>`).join("")}</tr>`).join(""); | |
| const palette = [COLORS.steel, COLORS.blue, COLORS.green, COLORS.amber]; | |
| destroyChart("execPlanning"); | |
| charts.execPlanning = new Chart($("execPlanningChart"), { | |
| type: "scatter", | |
| data: { datasets: result.rows.map((r, i) => ({ label: r.label, data: [{ x: r.mean_hbm_gb, y: r.p95_step_ttft_ms }], backgroundColor: palette[i], borderColor: palette[i], pointRadius: 7, pointHoverRadius: 9 })) }, | |
| options: { ...commonChartOptions(), plugins: { legend: pointLegend() }, scales: { x: { beginAtZero: true, title: { display: true, text: "Mean prefix HBM (GB)" } }, y: { beginAtZero: true, title: { display: true, text: "p95 step TTFT (ms)" } } } }, | |
| }); | |
| } | |
| $("execPlanningBtn").addEventListener("click", async () => { | |
| const button = $("execPlanningBtn"); button.disabled = true; button.textContent = "Comparing planners..."; | |
| try { renderExecutionPlanning(await callPython("execution_planning_study", { config: executionConfigFromUI() })); } | |
| catch (error) { reportError("Execution planning study failed", error); } | |
| finally { button.disabled = false; button.textContent = "Compare planning policies"; } | |
| }); | |
| $("execPlanningCopy").addEventListener("click", () => { if (lastExecutionPlanning) copyText(tableText(execPlanningHeaders, execPlanningTableRows(lastExecutionPlanning)), "Planning table copied"); }); | |
| $("execPlanningCsv").addEventListener("click", () => { if (lastExecutionPlanning) downloadCsv(`inferscale_execution-planning-study_${stamp()}.csv`, execPlanningHeaders, execPlanningTableRows(lastExecutionPlanning)); }); | |
| $("execPlanningJson").addEventListener("click", () => { if (lastExecutionPlanning) downloadNamedJson("inferscale_execution-planning-study", lastExecutionPlanning); }); | |
| const execHorizonHeaders = ["Horizon", "p95 TTFT", "Future-role recall@K", "Prefix hit", "Utilization", "Unused prefetch", "Mean HBM", "Saved prefill"]; | |
| function execHorizonTableRows(result) { | |
| return result.rows.map((r) => [r.horizon, `${fmt(r.p95_step_ttft_ms)} ms`, pct(r.forecast_recall), pct(r.prefix_hit_rate), r.prefetch_coverage ? pct(r.prefetch_utilization) : "N/A", `${fmt(r.unused_prefetch_gb, 4)} GB`, `${fmt(r.mean_hbm_gb, 4)} GB`, `${fmt(r.prefill_tokens_saved, 0)} tok`]); | |
| } | |
| function renderExecutionHorizon(result) { | |
| lastExecutionHorizon = result; | |
| $("execHorizonEmpty").classList.add("hidden"); $("execHorizonContent").classList.remove("hidden"); | |
| ["execHorizonCopy", "execHorizonCsv", "execHorizonJson"].forEach((id) => { $(id).disabled = false; }); | |
| $("execHorizonBestTtft").textContent = `${result.best_ttft_horizon ?? "N/A"} steps`; | |
| $("execHorizonBestUtil").textContent = `${result.best_utilization_horizon ?? "N/A"} steps`; | |
| $("execHorizonTopK").textContent = `${num("execTopK")} roles`; | |
| $("execHorizonBudget").textContent = `${fmt(num("execCacheBudget"), 2)}x`; | |
| $("execHorizonRows").innerHTML = execHorizonTableRows(result).map((row) => `<tr>${row.map((cell) => `<td>${escapeHtml(String(cell))}</td>`).join("")}</tr>`).join(""); | |
| destroyChart("execHorizon"); | |
| charts.execHorizon = new Chart($("execHorizonChart"), { | |
| type: "line", | |
| data: { datasets: [ | |
| { label: "p95 step TTFT", data: result.rows.map((r) => ({ x: r.horizon, y: r.p95_step_ttft_ms })), borderColor: COLORS.blue, backgroundColor: COLORS.blue, pointRadius: 4, yAxisID: "yLatency", fill: false }, | |
| { label: "Prefetch utilization", data: result.rows.map((r) => ({ x: r.horizon, y: r.prefetch_utilization * 100 })), borderColor: COLORS.green, backgroundColor: COLORS.green, pointRadius: 4, yAxisID: "yPct", fill: false }, | |
| { label: "Future-role recall@K", data: result.rows.map((r) => ({ x: r.horizon, y: r.forecast_recall * 100 })), borderColor: COLORS.amber, backgroundColor: COLORS.amber, pointRadius: 4, borderDash: [6, 4], yAxisID: "yPct", fill: false }, | |
| ] }, | |
| options: { ...commonChartOptions(), parsing: false, plugins: { legend: lineLegend() }, scales: { x: { type: "linear", min: 1, max: 5, ticks: { stepSize: 1 }, title: { display: true, text: "Forecast horizon (steps)" } }, yLatency: { position: "left", beginAtZero: true, title: { display: true, text: "p95 TTFT (ms)" } }, yPct: { position: "right", min: 0, max: 100, grid: { drawOnChartArea: false }, title: { display: true, text: "Recall / utilization (%)" } } } }, | |
| }); | |
| } | |
| $("execHorizonBtn").addEventListener("click", async () => { | |
| const button = $("execHorizonBtn"); button.disabled = true; button.textContent = "Sweeping horizons..."; | |
| try { renderExecutionHorizon(await callPython("execution_horizon_sweep", { config: executionConfigFromUI() })); } | |
| catch (error) { reportError("Forecast horizon sweep failed", error); } | |
| finally { button.disabled = false; button.textContent = "Sweep forecast horizon"; } | |
| }); | |
| $("execHorizonCopy").addEventListener("click", () => { if (lastExecutionHorizon) copyText(tableText(execHorizonHeaders, execHorizonTableRows(lastExecutionHorizon)), "Horizon table copied"); }); | |
| $("execHorizonCsv").addEventListener("click", () => { if (lastExecutionHorizon) downloadCsv(`inferscale_execution-horizon-sweep_${stamp()}.csv`, execHorizonHeaders, execHorizonTableRows(lastExecutionHorizon)); }); | |
| $("execHorizonJson").addEventListener("click", () => { if (lastExecutionHorizon) downloadNamedJson("inferscale_execution-horizon-sweep", lastExecutionHorizon); }); | |
| const execBudgetHeaders = ["Budget", "Policy", "p95 TTFT", "Future-role recall@K", "Prefix hit", "Utilization", "Mean HBM", "Unused prefetch", "Pressure evictions"]; | |
| function execBudgetTableRows(result) { | |
| return result.rows.map((r) => [`${fmt(r.budget, 2)}x`, r.policy_label, `${fmt(r.p95_step_ttft_ms)} ms`, pct(r.forecast_recall), pct(r.prefix_hit_rate), r.prefetch_coverage ? pct(r.prefetch_utilization) : "N/A", `${fmt(r.mean_hbm_gb, 4)} GB`, `${fmt(r.unused_prefetch_gb, 4)} GB`, fmt(r.pressure_evictions, 0)]); | |
| } | |
| function renderExecutionBudget(result) { | |
| lastExecutionBudget = result; | |
| $("execBudgetEmpty").classList.add("hidden"); $("execBudgetContent").classList.remove("hidden"); | |
| ["execBudgetCopy", "execBudgetCsv", "execBudgetJson"].forEach((id) => { $(id).disabled = false; }); | |
| $("execBudgetRows").innerHTML = execBudgetTableRows(result).map((row) => `<tr>${row.map((cell) => `<td>${escapeHtml(String(cell))}</td>`).join("")}</tr>`).join(""); | |
| const policyColors = { "Top-1": COLORS.steel, "Multi-step": COLORS.blue, "Utility-aware": COLORS.green }; | |
| const policies = ["Top-1", "Multi-step", "Utility-aware"]; | |
| destroyChart("execBudget"); | |
| charts.execBudget = new Chart($("execBudgetChart"), { | |
| type: "line", | |
| data: { datasets: policies.map((policy) => ({ label: policy, data: result.rows.filter((r) => r.policy_label === policy).map((r) => ({ x: r.budget, y: r.p95_step_ttft_ms })), borderColor: policyColors[policy], backgroundColor: policyColors[policy], pointRadius: 4, tension: .08, fill: false })) }, | |
| options: { ...commonChartOptions(), parsing: false, plugins: { legend: lineLegend() }, scales: { x: { type: "linear", min: .25, max: 1.05, title: { display: true, text: "Prefix-cache budget (working-set x)" } }, y: { beginAtZero: true, title: { display: true, text: "p95 step TTFT (ms)" } } } }, | |
| }); | |
| } | |
| $("execBudgetBtn").addEventListener("click", async () => { | |
| const button = $("execBudgetBtn"); button.disabled = true; button.textContent = "Sweeping cache budget..."; | |
| try { renderExecutionBudget(await callPython("execution_budget_sweep", { config: executionConfigFromUI() })); } | |
| catch (error) { reportError("Cache budget study failed", error); } | |
| finally { button.disabled = false; button.textContent = "Sweep cache budget"; } | |
| }); | |
| $("execBudgetCopy").addEventListener("click", () => { if (lastExecutionBudget) copyText(tableText(execBudgetHeaders, execBudgetTableRows(lastExecutionBudget)), "Cache budget table copied"); }); | |
| $("execBudgetCsv").addEventListener("click", () => { if (lastExecutionBudget) downloadCsv(`inferscale_execution-cache-budget-sweep_${stamp()}.csv`, execBudgetHeaders, execBudgetTableRows(lastExecutionBudget)); }); | |
| $("execBudgetJson").addEventListener("click", () => { if (lastExecutionBudget) downloadNamedJson("inferscale_execution-cache-budget-sweep", lastExecutionBudget); }); | |
| const consHeaders = ["Rank", "Policy", "Median TTFT", "95% CI of mean", "TTFT wins", "Pareto stable", "Oracle regret", "Worst seed", "Unused prefetch", "Mean HBM"]; | |
| function consTableRows(result) { | |
| return [...result.policies].sort((a, b) => a.robust_rank - b.robust_rank).map((r) => [ | |
| r.robust_rank, r.label, `${fmt(r.median_ttft_ms)} ms`, `${fmt(r.ttft_ci95_low_ms)}-${fmt(r.ttft_ci95_high_ms)} ms`, | |
| pct(r.ttft_win_rate), pct(r.pareto_stability), `${fmt(r.median_oracle_regret_ms)} ms`, `${fmt(r.worst_seed_ttft_ms)} ms`, | |
| `${fmt(r.mean_unused_prefetch_gb, 4)} GB`, `${fmt(r.mean_hbm_gb, 4)} GB`, | |
| ]); | |
| } | |
| function renderConsolidation(result) { | |
| lastConsolidation = result; | |
| $("consEmpty").classList.add("hidden"); $("consContent").classList.remove("hidden"); | |
| ["consCopy", "consCsv", "consJson", "consReportBtn"].forEach((id) => { $(id).disabled = false; }); | |
| $("consState").textContent = `${result.repetitions} matched seeds`; | |
| $("consState").className = "tag good"; | |
| $("consWinner").textContent = result.robust_winner || "N/A"; | |
| $("consNominal").textContent = result.nominal_winner_first_seed || "N/A"; | |
| $("consSeedCount").textContent = fmt(result.repetitions, 0); | |
| $("consOracle").textContent = `${fmt(result.oracle?.median_ttft_ms)} ms`; | |
| const rows = consTableRows(result); | |
| $("consRows").innerHTML = rows.map((row) => `<tr>${row.map((cell, idx) => `<td${idx === 1 ? "" : ""}>${escapeHtml(String(cell))}</td>`).join("")}</tr>`).join(""); | |
| $("consNote").textContent = result.note || ""; | |
| destroyChart("consolidation"); | |
| const ordered = [...result.policies].sort((a, b) => a.robust_rank - b.robust_rank); | |
| charts.consolidation = new Chart($("consChart"), { | |
| type: "scatter", | |
| data: { datasets: ordered.map((r, idx) => ({ | |
| label: r.label, | |
| data: [{ x: r.median_oracle_regret_ms, y: r.pareto_stability * 100 }], | |
| backgroundColor: [COLORS.blue, COLORS.green, COLORS.amber, COLORS.steel][idx % 4], | |
| borderColor: [COLORS.blue, COLORS.green, COLORS.amber, COLORS.steel][idx % 4], | |
| pointRadius: 7, | |
| })) }, | |
| options: { ...commonChartOptions(), parsing: false, plugins: { legend: pointLegend() }, scales: { | |
| x: { beginAtZero: true, title: { display: true, text: "Median regret to bounded offline oracle (ms)" } }, | |
| y: { min: 0, max: 100, title: { display: true, text: "Pareto stability across matched seeds (%)" } }, | |
| } }, | |
| }); | |
| } | |
| $("consRunBtn").addEventListener("click", async () => { | |
| const button = $("consRunBtn"); button.disabled = true; button.textContent = "Running matched seeds..."; | |
| $("consState").textContent = "Running"; $("consState").className = "tag"; | |
| try { | |
| renderConsolidation(await callPython("consolidation_study", { | |
| config: executionConfigFromUI(), repetitions: num("consSeeds"), bootstrap_samples: num("consBootstrap"), | |
| })); | |
| } catch (error) { reportError("Robust policy study failed", error); } | |
| finally { button.disabled = false; button.textContent = "Run robust policy study"; } | |
| }); | |
| $("consCopy").addEventListener("click", () => { if (lastConsolidation) copyText(tableText(consHeaders, consTableRows(lastConsolidation)), "Robust policy table copied"); }); | |
| $("consCsv").addEventListener("click", () => { if (lastConsolidation) downloadCsv(`inferscale_robust-policy-study_${stamp()}.csv`, consHeaders, consTableRows(lastConsolidation)); }); | |
| $("consJson").addEventListener("click", () => { if (lastConsolidation) downloadNamedJson("inferscale_robust-policy-study", lastConsolidation); }); | |
| $("measurementFile").addEventListener("change", async (event) => { | |
| const file = event.target.files?.[0]; | |
| if (!file) { measurementFileContent = ""; $("measurementCalibrateBtn").disabled = true; $("measurementStatus").textContent = "No measurement file loaded"; return; } | |
| try { | |
| measurementFileContent = await file.text(); | |
| $("measurementStatus").textContent = `${file.name} loaded (${measurementFileContent.length.toLocaleString()} characters)`; | |
| $("measurementCalibrateBtn").disabled = !runtimePill.classList.contains("ready"); | |
| } catch (error) { | |
| measurementFileContent = ""; $("measurementCalibrateBtn").disabled = true; | |
| $("measurementStatus").textContent = "File read failed"; reportError("Measurement file failed", error); | |
| } | |
| }); | |
| const measurementHeaders = ["Case", "Metric", "Measured", "Baseline pred.", "Calibrated pred.", "Baseline APE", "Calibrated APE"]; | |
| function measurementTableRows(calibration) { | |
| const baselineRows = calibration.baseline?.rows || []; | |
| const calibratedRows = calibration.calibrated?.rows || []; | |
| const byKey = new Map(calibratedRows.map((row) => [`${row.case}|${row.metric}`, row])); | |
| return baselineRows.map((base) => { | |
| const calibrated = byKey.get(`${base.case}|${base.metric}`) || {}; | |
| const unit = base.metric.includes("_ms") ? " ms" : base.metric.includes("rps") ? " req/s" : ""; | |
| return [base.case, base.metric, `${fmt(base.measured, 2)}${unit}`, `${fmt(base.predicted, 2)}${unit}`, `${fmt(calibrated.predicted, 2)}${unit}`, `${fmt(base.absolute_percentage_error, 2)}%`, `${fmt(calibrated.absolute_percentage_error, 2)}%`]; | |
| }); | |
| } | |
| function renderCalibration(imported, calibration) { | |
| lastMeasurementImport = imported; lastCalibration = calibration; | |
| $("measurementEmpty").classList.add("hidden"); $("measurementContent").classList.remove("hidden"); | |
| ["measurementCopy", "measurementCsv", "measurementJson"].forEach((id) => { $(id).disabled = false; }); | |
| if (lastConsolidation) $("consReportBtn").disabled = false; | |
| $("measurementState").textContent = calibration.validation_mode === "held-out" ? "Held-out" : "Resubstitution"; | |
| $("measurementState").className = `tag ${calibration.validation_mode === "held-out" ? "good" : ""}`; | |
| $("measurementCases").textContent = fmt(imported.case_count, 0); | |
| $("measurementPrefill").textContent = `${fmt(calibration.fitted_scales.prefill_time_scale, 3)}x`; | |
| $("measurementDecode").textContent = `${fmt(calibration.fitted_scales.decode_time_scale, 3)}x`; | |
| $("measurementMape").textContent = `${fmt(calibration.calibrated.mape_pct, 2)}%`; | |
| $("measurementCaption").textContent = `${calibration.train_count} train / ${calibration.holdout_count} validation cases`; | |
| const rows = measurementTableRows(calibration); | |
| $("measurementRows").innerHTML = rows.map((row) => `<tr>${row.map((cell) => `<td>${escapeHtml(String(cell))}</td>`).join("")}</tr>`).join(""); | |
| $("measurementNote").textContent = `${calibration.note} Baseline MAPE ${fmt(calibration.baseline.mape_pct, 2)}%; calibrated MAPE ${fmt(calibration.calibrated.mape_pct, 2)}%.`; | |
| } | |
| $("measurementCalibrateBtn").addEventListener("click", async () => { | |
| if (!measurementFileContent) return; | |
| const button = $("measurementCalibrateBtn"); button.disabled = true; button.textContent = "Importing measurements..."; | |
| try { | |
| const imported = await callPython("measurement_import", { | |
| content: measurementFileContent, source: $("measurementSource").value, base_config: configFromUI(), | |
| }); | |
| button.textContent = "Calibrating + validating..."; | |
| const calibrated = await callPython("measurement_calibrate", { | |
| cases: imported.cases, holdout_fraction: num("measurementHoldout"), seed: num("seed"), | |
| }); | |
| renderCalibration(imported, calibrated); | |
| } catch (error) { reportError("Measurement calibration failed", error); } | |
| finally { button.disabled = false; button.textContent = "Import and calibrate"; } | |
| }); | |
| $("measurementCopy").addEventListener("click", () => { if (lastCalibration) copyText(tableText(measurementHeaders, measurementTableRows(lastCalibration)), "Validation table copied"); }); | |
| $("measurementCsv").addEventListener("click", () => { if (lastCalibration) downloadCsv(`inferscale_heldout-validation_${stamp()}.csv`, measurementHeaders, measurementTableRows(lastCalibration)); }); | |
| $("measurementJson").addEventListener("click", () => { if (lastCalibration) downloadNamedJson("inferscale_measurement-calibration", { imported: lastMeasurementImport, calibration: lastCalibration }); }); | |
| $("consReportBtn").addEventListener("click", async () => { | |
| if (!lastConsolidation) return; | |
| const button = $("consReportBtn"); button.disabled = true; button.textContent = "Generating report..."; | |
| try { | |
| const result = await callPython("research_report", { robust: lastConsolidation, calibration: lastCalibration }); | |
| downloadTextFile(`inferscale_research-consolidation_${stamp()}.md`, result.markdown, "text/markdown;charset=utf-8"); | |
| } catch (error) { reportError("Report generation failed", error); } | |
| finally { button.disabled = false; button.textContent = "Download Markdown report"; } | |
| }); | |
| function syncAgentControls() { | |
| const retention = $("agentRetention").value; | |
| const routing = $("agentRouting").value; | |
| const adaptive = retention === "adaptive"; | |
| const ttl = retention === "ttl" || adaptive; | |
| const hostTier = retention === "offload" || retention === "gap_aware" || adaptive; | |
| const gapAware = retention === "gap_aware" || adaptive; | |
| const bounded = routing === "bounded_affinity"; | |
| $("agentTtl").disabled = !ttl; | |
| $("agentTtlLabel").classList.toggle("field-disabled", !ttl); | |
| $("agentAffinitySlack").disabled = !bounded; | |
| $("agentAffinitySlackLabel").classList.toggle("field-disabled", !bounded); | |
| $("agentGapThreshold").disabled = !gapAware; | |
| $("agentGapThresholdLabel").classList.toggle("field-disabled", !gapAware); | |
| ["agentHostMemory", "agentHostBandwidth", "agentHostBase"].forEach((id) => { $(id).disabled = !hostTier; }); | |
| ["agentHostMemoryLabel", "agentHostBandwidthLabel", "agentHostBaseLabel"].forEach((id) => { $(id).classList.toggle("field-disabled", !hostTier); }); | |
| ["agentPredictorScope", "agentPredictorAlpha", "agentPredictorMin"].forEach((id) => { $(id).disabled = !adaptive; }); | |
| ["agentPredictorScopeLabel", "agentPredictorAlphaLabel", "agentPredictorMinLabel"].forEach((id) => { $(id).classList.toggle("field-disabled", !adaptive); }); | |
| } | |
| $("agentRetention").addEventListener("change", syncAgentControls); | |
| $("agentRouting").addEventListener("change", syncAgentControls); | |
| syncAgentControls(); | |
| function normalizeTraceRows(rows) { | |
| if (!Array.isArray(rows)) throw new Error("Trace JSON must be an array or contain a requests array"); | |
| if (rows.length > 10000) throw new Error("Trace replay is limited to 10,000 requests in the browser"); | |
| return rows.map((row, index) => { | |
| const arrival = Number(row.arrival_time); | |
| const prompt = Number(row.prompt_tokens); | |
| const output = Number(row.output_tokens); | |
| if (!Number.isFinite(arrival) || arrival < 0 || !Number.isFinite(prompt) || prompt < 1 || !Number.isFinite(output) || output < 1) { | |
| throw new Error(`Invalid trace row ${index + 1}`); | |
| } | |
| return { arrival_time: arrival, prompt_tokens: Math.round(prompt), output_tokens: Math.round(output) }; | |
| }); | |
| } | |
| function parseTraceCsv(text) { | |
| const lines = text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean); | |
| if (lines.length < 2) throw new Error("CSV trace needs a header and at least one request"); | |
| const headers = lines[0].split(",").map((x) => x.trim()); | |
| const required = ["arrival_time", "prompt_tokens", "output_tokens"]; | |
| const idx = Object.fromEntries(required.map((name) => [name, headers.indexOf(name)])); | |
| if (required.some((name) => idx[name] < 0)) throw new Error("CSV header must contain arrival_time,prompt_tokens,output_tokens"); | |
| return normalizeTraceRows(lines.slice(1).map((line) => { | |
| const cells = line.split(",").map((x) => x.trim()); | |
| return { arrival_time: cells[idx.arrival_time], prompt_tokens: cells[idx.prompt_tokens], output_tokens: cells[idx.output_tokens] }; | |
| })); | |
| } | |
| async function loadTraceFile(file) { | |
| const text = await file.text(); | |
| let rows; | |
| if (file.name.toLowerCase().endsWith(".json")) { | |
| const parsed = JSON.parse(text); | |
| rows = normalizeTraceRows(Array.isArray(parsed) ? parsed : parsed.requests); | |
| } else { | |
| rows = parseTraceCsv(text); | |
| } | |
| rows.sort((a, b) => a.arrival_time - b.arrival_time); | |
| traceRequests = rows; | |
| const span = rows.length ? rows[rows.length - 1].arrival_time - rows[0].arrival_time : 0; | |
| $("traceStatus").textContent = `${rows.length.toLocaleString()} requests loaded (${fmt(span, 2)} s span)`; | |
| showToast("Trace loaded"); | |
| } | |
| $("traceFile").addEventListener("change", async (event) => { | |
| const file = event.target.files?.[0]; | |
| if (!file) return; | |
| try { await loadTraceFile(file); } | |
| catch (error) { traceRequests = []; $("traceStatus").textContent = "Trace rejected"; reportError("Trace load failed", error); } | |
| }); | |
| $("traceClearBtn").addEventListener("click", () => { | |
| traceRequests = []; | |
| $("traceFile").value = ""; | |
| $("traceStatus").textContent = "No trace loaded"; | |
| }); | |
| function syncConditionalControls() { | |
| const pd = $("topology").value === "disaggregated_pd"; | |
| $("pdControls").classList.toggle("hidden", !pd); | |
| $("colocatedAcceleratorLabel").classList.toggle("hidden", pd); | |
| if (pd && $("scheduler").value === "static_fcfs") { | |
| $("scheduler").value = "continuous_fcfs"; | |
| showToast("Static FCFS switched to Continuous FCFS for P/D topology"); | |
| } | |
| $("prefixControls").classList.toggle("hidden", !boolSelect("prefixCache")); | |
| const trace = $("arrival").value === "trace"; | |
| $("burstControls").classList.toggle("hidden", $("arrival").value !== "bursty"); | |
| $("traceControls").classList.toggle("hidden", !trace); | |
| ["rate", "duration", "promptMean", "promptCv", "outputMean", "outputCv"].forEach((id) => { $(id).disabled = trace; }); | |
| $("capacityBtn").disabled = trace || !runtimePill.classList.contains("ready"); | |
| $("capacityBtn").title = trace ? "Capacity search is undefined for a fixed arrival trace" : ""; | |
| } | |
| $("topology").addEventListener("change", syncConditionalControls); | |
| $("prefixCache").addEventListener("change", syncConditionalControls); | |
| $("arrival").addEventListener("change", syncConditionalControls); | |
| syncConditionalControls(); | |
| const tabButtons = [...document.querySelectorAll(".tab")]; | |
| function activateTab(tab) { | |
| for (const item of tabButtons) { | |
| const active = item === tab; | |
| item.classList.toggle("active", active); | |
| item.setAttribute("aria-selected", active ? "true" : "false"); | |
| item.tabIndex = active ? 0 : -1; | |
| } | |
| document.querySelectorAll(".tab-panel").forEach((panel) => { | |
| panel.classList.remove("active"); | |
| panel.setAttribute("aria-hidden", "true"); | |
| }); | |
| const targetPanel = $(tab.dataset.tab); | |
| targetPanel.classList.add("active"); | |
| targetPanel.setAttribute("aria-hidden", "false"); | |
| setTimeout(() => Object.values(charts).forEach((chart) => chart.resize()), 20); | |
| } | |
| for (const tab of tabButtons) { | |
| tab.addEventListener("click", () => activateTab(tab)); | |
| tab.addEventListener("keydown", (event) => { | |
| if (!["ArrowLeft", "ArrowRight", "Home", "End"].includes(event.key)) return; | |
| event.preventDefault(); | |
| const current = tabButtons.indexOf(tab); | |
| let next = current; | |
| if (event.key === "ArrowLeft") next = (current - 1 + tabButtons.length) % tabButtons.length; | |
| if (event.key === "ArrowRight") next = (current + 1) % tabButtons.length; | |
| if (event.key === "Home") next = 0; | |
| if (event.key === "End") next = tabButtons.length - 1; | |
| tabButtons[next].focus(); | |
| activateTab(tabButtons[next]); | |
| }); | |
| } | |
| function closeExpandedChart() { | |
| const card = document.querySelector(".chart-card.chart-expanded"); | |
| if (!card) return; | |
| card.classList.remove("chart-expanded"); | |
| const button = card.querySelector(".chart-expand"); | |
| if (button) button.textContent = "Expand"; | |
| document.body.classList.remove("chart-open"); | |
| setTimeout(() => Chart.getChart(card.querySelector("canvas"))?.resize(), 20); | |
| } | |
| for (const button of document.querySelectorAll(".chart-expand")) { | |
| button.addEventListener("click", () => { | |
| const card = button.closest(".chart-card"); | |
| const wasExpanded = card.classList.contains("chart-expanded"); | |
| closeExpandedChart(); | |
| if (!wasExpanded) { | |
| card.classList.add("chart-expanded"); | |
| button.textContent = "Close"; | |
| document.body.classList.add("chart-open"); | |
| setTimeout(() => Chart.getChart(card.querySelector("canvas"))?.resize(), 20); | |
| } | |
| }); | |
| } | |
| for (const button of document.querySelectorAll(".chart-download")) { | |
| button.addEventListener("click", () => downloadChart(button.closest(".chart-card"))); | |
| } | |
| document.addEventListener("keydown", (event) => { if (event.key === "Escape") closeExpandedChart(); }); | |