Spaces:
Running
Running
| const DATA = window.LOCAL_FRONTIER_DATA; | |
| const MODEL_DATA = window.LOCAL_FRONTIER_MODEL_DATA || { | |
| assumptions: {}, | |
| models: [], | |
| }; | |
| const PROFILE_DATA = window.LOCAL_FRONTIER_PROFILE_DATA || { | |
| profiles: [], | |
| }; | |
| const byId = new Map(DATA.items.map((item) => [item.id, item])); | |
| const accelerators = DATA.accelerators || []; | |
| const acceleratorById = new Map( | |
| accelerators.map((accelerator) => [accelerator.id, accelerator]), | |
| ); | |
| const systemVendors = DATA.system_vendors || []; | |
| const systemVendorBySlug = new Map( | |
| systemVendors.map((vendor) => [vendor.slug, vendor]), | |
| ); | |
| const models = MODEL_DATA.models || []; | |
| const modelById = new Map(models.map((model) => [model.id, model])); | |
| const profileByRepo = new Map( | |
| (PROFILE_DATA.profiles || []).map((profile) => [profile.repo, profile]), | |
| ); | |
| const modelLookup = new Map(); | |
| for (const model of models) { | |
| modelLookup.set(model.id, model); | |
| for (const alias of model.aliases || []) { | |
| modelLookup.set(alias, model); | |
| } | |
| } | |
| const BOUNDS_ENGINE = window.LocalFrontierBounds; | |
| if (!BOUNDS_ENGINE?.calculateBounds || !BOUNDS_ENGINE?.calculateProfiledBounds) { | |
| throw new Error("LocalFrontierBounds engine is not loaded."); | |
| } | |
| const producers = DATA.producers; | |
| const producerColors = { | |
| Apple: "var(--apple)", | |
| NVIDIA: "var(--nvidia)", | |
| AMD: "var(--amd)", | |
| "Best-so-far": "var(--accent)", | |
| "Under 3k observed": "var(--blue)", | |
| "Under 3k fit": "var(--blue)", | |
| "3k-5k observed": "var(--green)", | |
| "3k-5k fit": "var(--green)", | |
| "5k-10k observed": "var(--red)", | |
| "5k-10k fit": "var(--red)", | |
| }; | |
| function $(selector, root = document) { | |
| return root.querySelector(selector); | |
| } | |
| function escapeHTML(value) { | |
| return String(value ?? "").replace( | |
| /[&<>"']/g, | |
| (char) => | |
| ({ | |
| "&": "&", | |
| "<": "<", | |
| ">": ">", | |
| '"': """, | |
| "'": "'", | |
| })[char], | |
| ); | |
| } | |
| function fmtMoney(value) { | |
| if (value === null || value === undefined || value === "") return "-"; | |
| return `$${Number(value).toLocaleString("en-US", { maximumFractionDigits: 0 })}`; | |
| } | |
| function fmtNumber(value, digits = 0) { | |
| if (value === null || value === undefined || value === "") return "-"; | |
| return Number(value).toLocaleString("en-US", { | |
| maximumFractionDigits: digits, | |
| minimumFractionDigits: 0, | |
| }); | |
| } | |
| function fmtTokenCount(value) { | |
| return value === null || value === undefined || value === "" | |
| ? "Unknown" | |
| : `${fmtNumber(value)} tokens`; | |
| } | |
| function recordedContextLimit(model) { | |
| const value = Number(model?.architecture?.max_context_tokens); | |
| return Number.isInteger(value) && value > 0 ? value : null; | |
| } | |
| function fmtGB(value) { | |
| return value === null || value === undefined || value === "" | |
| ? "-" | |
| : `${fmtNumber(value)} GB`; | |
| } | |
| function fmtBandwidth(value) { | |
| return value === null || value === undefined || value === "" | |
| ? "-" | |
| : `${fmtNumber(value)} GB/s`; | |
| } | |
| function fmtGBDecimal(value, digits = 2) { | |
| if ( | |
| value === null || | |
| value === undefined || | |
| value === "" || | |
| !Number.isFinite(Number(value)) | |
| ) | |
| return "-"; | |
| return `${fmtNumber(value, digits)} GB`; | |
| } | |
| function fmtTok(value, digits = 0) { | |
| if ( | |
| value === null || | |
| value === undefined || | |
| value === "" || | |
| !Number.isFinite(Number(value)) | |
| ) | |
| return "-"; | |
| return `${fmtNumber(value, digits)} tok/s`; | |
| } | |
| function fmtParams(value) { | |
| if (value === null || value === undefined || value === "") return "-"; | |
| return `${fmtNumber(value, 1)}B`; | |
| } | |
| function fmtUsdPerGB(value) { | |
| return value === null || value === undefined || value === "" | |
| ? "-" | |
| : `$${fmtNumber(value, 2)}/GB`; | |
| } | |
| function deviceHref(id, prefix = "") { | |
| return `/devices/${id}`; | |
| } | |
| function pathFromModelId(id) { | |
| return String(id).split("/").map(encodeURIComponent).join("/"); | |
| } | |
| function modelHref(id, prefix = "") { | |
| return `/models/${pathFromModelId(id)}`; | |
| } | |
| function huggingFaceModelHref(model) { | |
| return `https://huggingface.co/${pathFromModelId(model.id)}`; | |
| } | |
| function idList(value) { | |
| const raw = Array.isArray(value) ? value : value ? [value] : []; | |
| const ids = []; | |
| for (const item of raw) { | |
| for (const part of String(item).split(",")) { | |
| const id = part.trim(); | |
| if (id && !ids.includes(id)) ids.push(id); | |
| } | |
| } | |
| return ids; | |
| } | |
| function compareHref(modelIds = [], hardwareIds = []) { | |
| const modelPart = idList(modelIds).map(encodeURIComponent).join(","); | |
| const hardwarePart = idList(hardwareIds).map(encodeURIComponent).join(","); | |
| const params = []; | |
| if (modelPart) params.push(`models=${modelPart}`); | |
| if (hardwarePart) params.push(`hardware=${hardwarePart}`); | |
| return params.length ? `/compare?${params.join("&")}` : "/compare"; | |
| } | |
| function acceleratorHref(accelerator, prefix = "") { | |
| return `/accelerators/${accelerator.vendor_slug}/${accelerator.slug}`; | |
| } | |
| function acceleratorVendorHref(vendorSlug, prefix = "") { | |
| return `/accelerators/${vendorSlug}`; | |
| } | |
| function acceleratorVendorLink(vendor, prefix = "") { | |
| const vendorSlug = String(vendor || "").toLowerCase(); | |
| return `<a class="device-link producer-${escapeHTML(vendor)}" href="${acceleratorVendorHref(vendorSlug, prefix)}">${escapeHTML(vendor)}</a>`; | |
| } | |
| function systemVendorHref(vendorSlug, prefix = "") { | |
| return `/systems/${vendorSlug}`; | |
| } | |
| function systemVendorLink(name, slug, prefix = "") { | |
| return `<a class="device-link" href="${systemVendorHref(slug, prefix)}">${escapeHTML(name)}</a>`; | |
| } | |
| function currentPriceEvent(item) { | |
| return [...item.price_history] | |
| .sort( | |
| (a, b) => | |
| a.end_year - b.end_year || | |
| a.start_year - b.start_year || | |
| a.catalog_order - b.catalog_order, | |
| ) | |
| .at(-1); | |
| } | |
| function isEstimatedPrice(event) { | |
| return ( | |
| event.evidence !== "quoted" || | |
| event.price_kind === "reconstructed_config" || | |
| event.price_kind === "component_bom" | |
| ); | |
| } | |
| function estimateMark(event) { | |
| return isEstimatedPrice(event) | |
| ? ` <span class="estimate-mark" title="Estimated or reconstructed price">≈</span>` | |
| : ""; | |
| } | |
| function homeStats() { | |
| return [ | |
| { value: String(DATA.stats.item_count), label: "source item pages" }, | |
| { | |
| value: String(DATA.stats.catalog_row_count), | |
| label: "priced catalog rows", | |
| }, | |
| { | |
| value: `${DATA.stats.max_memory_gb}GB`, | |
| label: "maximum accelerator memory", | |
| }, | |
| { | |
| value: `${DATA.stats.best_under_5k_memory_gb}GB`, | |
| label: "best current memory under $5k", | |
| }, | |
| ]; | |
| } | |
| function homeMetricOptions() { | |
| return [ | |
| { | |
| value: "annual_memory", | |
| label: "Annual memory by accelerator vendor", | |
| }, | |
| { | |
| value: "annual_price_per_gb", | |
| label: "Annual complete-system USD/GB", | |
| }, | |
| { value: "annual_bandwidth", label: "Annual memory bandwidth" }, | |
| { value: "best_memory", label: "Best-so-far memory" }, | |
| { | |
| value: "best_price_per_gb", | |
| label: "Best-so-far complete-system USD/GB", | |
| }, | |
| { value: "best_bandwidth", label: "Best-so-far memory bandwidth" }, | |
| { value: "projection", label: "Consumer memory projection to 2030" }, | |
| ]; | |
| } | |
| function hardwareBridgeState() { | |
| return { | |
| rows: DATA.items.map(hardwareDatabaseRow), | |
| producers: [ | |
| { value: "all", label: "All accelerator vendors" }, | |
| ...producers.map((producer) => ({ value: producer, label: producer })), | |
| ], | |
| priceBands: [ | |
| { value: "all", label: "All bands" }, | |
| ...DATA.bands.map((band) => ({ value: band.key, label: band.label })), | |
| ], | |
| }; | |
| } | |
| function renderStats() { | |
| const host = $("#statStrip"); | |
| if (!host) return; | |
| const stats = homeStats(); | |
| host.innerHTML = ` | |
| <table class="stat-table"> | |
| <tbody> | |
| ${stats | |
| .map( | |
| ({ value, label }) => ` | |
| <tr> | |
| <th scope="row">${escapeHTML(label)}</th> | |
| <td>${escapeHTML(value)}</td> | |
| </tr> | |
| `, | |
| ) | |
| .join("")} | |
| </tbody> | |
| </table> | |
| `; | |
| } | |
| function setupControls() { | |
| const bandSelect = $("#bandSelect"); | |
| const tableBand = $("#tableBandFilter"); | |
| if (bandSelect) { | |
| bandSelect.innerHTML = DATA.bands | |
| .map( | |
| (band) => | |
| `<option value="${band.key}">${escapeHTML(band.label)}</option>`, | |
| ) | |
| .join(""); | |
| bandSelect.value = "3000_5000"; | |
| } | |
| if (tableBand) { | |
| tableBand.innerHTML += DATA.bands | |
| .map( | |
| (band) => | |
| `<option value="${band.key}">${escapeHTML(band.label)}</option>`, | |
| ) | |
| .join(""); | |
| } | |
| const producerFilter = $("#producerFilter"); | |
| if (producerFilter) { | |
| producerFilter.innerHTML += producers | |
| .map((producer) => `<option value="${producer}">${producer}</option>`) | |
| .join(""); | |
| } | |
| const toggle = $("#producerToggle"); | |
| if (toggle) { | |
| toggle.innerHTML = producers | |
| .map( | |
| (producer) => | |
| `<button class="chip" type="button" data-producer="${producer}" aria-pressed="true">${producer}</button>`, | |
| ) | |
| .join(""); | |
| toggle.addEventListener("click", (event) => { | |
| const button = event.target.closest("button[data-producer]"); | |
| if (!button) return; | |
| button.setAttribute( | |
| "aria-pressed", | |
| button.getAttribute("aria-pressed") !== "true", | |
| ); | |
| renderFrontierChart(); | |
| }); | |
| } | |
| ["metricSelect", "bandSelect"].forEach((id) => { | |
| const el = $(`#${id}`); | |
| if (el) el.addEventListener("change", renderFrontierChart); | |
| }); | |
| ["deviceSearch", "producerFilter", "tableBandFilter"].forEach((id) => { | |
| const el = $(`#${id}`); | |
| if (el) el.addEventListener("input", renderDeviceTable); | |
| }); | |
| } | |
| function activeProducers() { | |
| return [...document.querySelectorAll("#producerToggle button")] | |
| .filter((button) => button.getAttribute("aria-pressed") === "true") | |
| .map((button) => button.dataset.producer); | |
| } | |
| function metricConfig(metric, band, active = activeProducers()) { | |
| if (metric === "projection") { | |
| const labels = { | |
| under_3000: "Under 3k", | |
| "3000_5000": "3k-5k", | |
| "5000_10000": "5k-10k", | |
| }; | |
| const series = []; | |
| for (const [bandKey, label] of Object.entries(labels)) { | |
| const rows = DATA.projection.filter((row) => row.price_band === bandKey); | |
| series.push({ | |
| label: `${label} observed`, | |
| colorKey: `${label} observed`, | |
| dash: false, | |
| points: rows | |
| .filter((row) => row.observed_best_so_far_memory_gb !== null) | |
| .map((row) => ({ | |
| x: row.year, | |
| y: row.observed_best_so_far_memory_gb, | |
| row, | |
| detail: `${label}: observed best-so-far`, | |
| })), | |
| }); | |
| series.push({ | |
| label: `${label} linear fit`, | |
| colorKey: `${label} fit`, | |
| dash: true, | |
| points: rows.map((row) => ({ | |
| x: row.year, | |
| y: row.linear_fit_memory_gb, | |
| row, | |
| detail: `${label}: linear scenario`, | |
| })), | |
| }); | |
| } | |
| return { | |
| title: "Consumer-band best-so-far memory projection", | |
| subtitle: | |
| "Least-squares linear scenario fitted to 2020-2026 best-so-far memory. Not verified future product data.", | |
| yLabel: "Memory capacity", | |
| yFormat: fmtGB, | |
| series, | |
| }; | |
| } | |
| const bandLabel = DATA.bands.find((b) => b.key === band)?.label || band; | |
| if (metric.startsWith("annual_")) { | |
| const source = DATA.annual.filter( | |
| (row) => | |
| row.price_band === band && | |
| row.memory_gb !== null && | |
| active.includes(row.producer), | |
| ); | |
| const metricMap = { | |
| annual_memory: [ | |
| "memory_gb", | |
| "Annual memory by accelerator vendor", | |
| "Memory capacity", | |
| fmtGB, | |
| ], | |
| annual_price_per_gb: [ | |
| "price_per_memory_gb", | |
| "Annual complete-system USD per GB", | |
| "USD per GB", | |
| fmtUsdPerGB, | |
| ], | |
| annual_bandwidth: [ | |
| "bandwidth_gbps", | |
| "Annual memory bandwidth", | |
| "Bandwidth", | |
| fmtBandwidth, | |
| ], | |
| }; | |
| const [field, title, yLabel, yFormat] = metricMap[metric]; | |
| return { | |
| title: `${title} - ${bandLabel}`, | |
| subtitle: | |
| "Annual maximum-memory row for each accelerator vendor in the selected price band.", | |
| yLabel, | |
| yFormat, | |
| series: producers | |
| .filter((producer) => active.includes(producer)) | |
| .map((producer) => ({ | |
| label: producer, | |
| colorKey: producer, | |
| points: source | |
| .filter((row) => row.producer === producer && row[field] !== null) | |
| .map((row) => ({ | |
| x: row.year, | |
| y: row[field], | |
| row, | |
| detail: row.system, | |
| })), | |
| })) | |
| .filter((entry) => entry.points.length), | |
| }; | |
| } | |
| const bestMap = { | |
| best_memory: [ | |
| DATA.dominant, | |
| "memory_gb", | |
| "Best-so-far memory", | |
| "Memory capacity", | |
| fmtGB, | |
| ], | |
| best_price_per_gb: [ | |
| DATA.best_price, | |
| "metric_value", | |
| "Best-so-far complete-system USD per GB", | |
| "USD per GB", | |
| fmtUsdPerGB, | |
| ], | |
| best_bandwidth: [ | |
| DATA.best_bandwidth, | |
| "metric_value", | |
| "Best-so-far memory bandwidth", | |
| "Bandwidth", | |
| fmtBandwidth, | |
| ], | |
| }; | |
| const [rows, field, title, yLabel, yFormat] = bestMap[metric]; | |
| return { | |
| title: `${title} - ${bandLabel}`, | |
| subtitle: | |
| "The line carries the previous best forward until a better source-backed point appears.", | |
| yLabel, | |
| yFormat, | |
| series: [ | |
| { | |
| label: "Best-so-far", | |
| colorKey: "Best-so-far", | |
| points: rows | |
| .filter((row) => row.price_band === band && row[field] !== null) | |
| .map((row) => ({ | |
| x: row.year, | |
| y: row[field], | |
| row, | |
| detail: row.system, | |
| })), | |
| }, | |
| ], | |
| }; | |
| } | |
| function renderFrontierChart() { | |
| const host = $("#frontierChart"); | |
| if (!host) return; | |
| const metric = $("#metricSelect").value; | |
| const band = $("#bandSelect").value; | |
| $("#bandSelect").disabled = metric === "projection"; | |
| const config = metricConfig(metric, band); | |
| renderChart(host, config); | |
| } | |
| function renderChart(host, config) { | |
| const width = 1120; | |
| const height = 560; | |
| const margin = { top: 92, right: 42, bottom: 72, left: 86 }; | |
| const allPoints = config.series.flatMap((series) => series.points); | |
| if (!allPoints.length) { | |
| host.innerHTML = `<div class="empty">No source-backed rows for this selection.</div>`; | |
| return; | |
| } | |
| const xValues = allPoints.map((point) => point.x); | |
| const yValues = allPoints | |
| .map((point) => point.y) | |
| .filter((value) => Number.isFinite(value)); | |
| const xMin = Math.min(...xValues); | |
| const xMax = Math.max(...xValues); | |
| const yMin = Math.min(0, Math.min(...yValues)); | |
| const yMax = Math.max(...yValues) * 1.16; | |
| const plotW = width - margin.left - margin.right; | |
| const plotH = height - margin.top - margin.bottom; | |
| const xScale = (x) => | |
| margin.left + ((x - xMin) / Math.max(1, xMax - xMin)) * plotW; | |
| const yScale = (y) => | |
| margin.top + plotH - ((y - yMin) / Math.max(1, yMax - yMin)) * plotH; | |
| const years = []; | |
| for (let year = xMin; year <= xMax; year += 1) years.push(year); | |
| const tickCount = 5; | |
| const yTicks = Array.from( | |
| { length: tickCount }, | |
| (_, index) => yMin + ((yMax - yMin) / (tickCount - 1)) * index, | |
| ); | |
| const linePath = (points) => | |
| points | |
| .filter((point) => Number.isFinite(point.y)) | |
| .map( | |
| (point, index) => | |
| `${index === 0 ? "M" : "L"} ${xScale(point.x).toFixed(2)} ${yScale(point.y).toFixed(2)}`, | |
| ) | |
| .join(" "); | |
| const seriesSvg = config.series | |
| .map((series) => { | |
| const color = producerColors[series.colorKey] || "var(--accent)"; | |
| const dash = series.dash ? ' stroke-dasharray="8 8"' : ""; | |
| const path = linePath(series.points); | |
| const points = series.points | |
| .map((point) => { | |
| const itemId = point.row?.item_id || ""; | |
| return `<circle class="chart-point" data-item="${escapeHTML(itemId)}" data-label="${escapeHTML(series.label)}" data-detail="${escapeHTML(point.detail || "")}" data-x="${point.x}" data-y="${point.y}" cx="${xScale(point.x).toFixed(2)}" cy="${yScale(point.y).toFixed(2)}" r="5.8" fill="${color}" stroke="var(--bg)" stroke-width="1.5"></circle>`; | |
| }) | |
| .join(""); | |
| return `<path d="${path}" fill="none" stroke="${color}" stroke-width="3"${dash}></path>${points}`; | |
| }) | |
| .join(""); | |
| const legend = config.series | |
| .map((series, index) => { | |
| const color = producerColors[series.colorKey] || "var(--accent)"; | |
| const x = margin.left + (index % 3) * 260; | |
| const y = 58 + Math.floor(index / 3) * 24; | |
| return `<g transform="translate(${x} ${y})"><line x1="0" x2="24" y1="0" y2="0" stroke="${color}" stroke-width="3"${series.dash ? ' stroke-dasharray="6 6"' : ""}></line><text x="34" y="4" fill="var(--muted)" font-size="13">${escapeHTML(series.label)}</text></g>`; | |
| }) | |
| .join(""); | |
| host.innerHTML = ` | |
| <div class="chart-title"> | |
| <h3>${escapeHTML(config.title)}</h3> | |
| <p class="chart-subtitle">${escapeHTML(config.subtitle)}</p> | |
| </div> | |
| <svg viewBox="0 0 ${width} ${height}" role="img" aria-label="${escapeHTML(config.title)}"> | |
| <g class="grid"> | |
| ${yTicks.map((tick) => `<line x1="${margin.left}" x2="${width - margin.right}" y1="${yScale(tick).toFixed(2)}" y2="${yScale(tick).toFixed(2)}" stroke="var(--line)" stroke-width="1"></line>`).join("")} | |
| </g> | |
| <g class="axes"> | |
| <line x1="${margin.left}" x2="${width - margin.right}" y1="${height - margin.bottom}" y2="${height - margin.bottom}" stroke="var(--line-strong)"></line> | |
| <line x1="${margin.left}" x2="${margin.left}" y1="${margin.top}" y2="${height - margin.bottom}" stroke="var(--line-strong)"></line> | |
| </g> | |
| <g class="ticks"> | |
| ${years.map((year) => `<text x="${xScale(year).toFixed(2)}" y="${height - margin.bottom + 28}" text-anchor="middle" fill="var(--muted)" font-size="13">${year}</text>`).join("")} | |
| ${yTicks.map((tick) => `<text x="${margin.left - 14}" y="${yScale(tick).toFixed(2) + 4}" text-anchor="end" fill="var(--muted)" font-size="13">${escapeHTML(config.yFormat(tick))}</text>`).join("")} | |
| </g> | |
| <text x="${margin.left}" y="${height - 24}" fill="var(--faint)" font-size="13">Year</text> | |
| <text x="24" y="${margin.top}" fill="var(--faint)" font-size="13" transform="rotate(-90 24 ${margin.top})">${escapeHTML(config.yLabel)}</text> | |
| <g class="series">${seriesSvg}</g> | |
| <g class="legend">${legend}</g> | |
| </svg> | |
| <div class="tooltip" hidden></div> | |
| `; | |
| const tooltip = $(".tooltip", host); | |
| host.querySelectorAll(".chart-point").forEach((point) => { | |
| point.addEventListener("mouseenter", () => { | |
| const item = byId.get(point.dataset.item); | |
| tooltip.hidden = false; | |
| tooltip.innerHTML = ` | |
| <strong>${escapeHTML(point.dataset.label)}: ${escapeHTML(config.yFormat(point.dataset.y))}</strong> | |
| <span>${escapeHTML(point.dataset.x)} ${point.dataset.detail ? "- " + escapeHTML(point.dataset.detail) : ""}</span> | |
| ${item ? `<span>${escapeHTML(item.name)}</span><span>Click to open item page.</span>` : ""} | |
| `; | |
| const box = point.getBoundingClientRect(); | |
| const hostBox = host.getBoundingClientRect(); | |
| tooltip.style.left = `${box.left - hostBox.left + box.width / 2}px`; | |
| tooltip.style.top = `${box.top - hostBox.top}px`; | |
| }); | |
| point.addEventListener("mouseleave", () => { | |
| tooltip.hidden = true; | |
| }); | |
| point.addEventListener("click", () => { | |
| if (point.dataset.item) { | |
| window.location.href = deviceHref(point.dataset.item); | |
| } | |
| }); | |
| }); | |
| } | |
| function renderDeviceTable() { | |
| const host = $("#deviceTable"); | |
| if (!host) return; | |
| const search = ($("#deviceSearch")?.value || "").trim().toLowerCase(); | |
| const producer = $("#producerFilter")?.value || "all"; | |
| const band = $("#tableBandFilter")?.value || "all"; | |
| const rows = DATA.items.filter((item) => { | |
| const haystack = [ | |
| item.name, | |
| item.system_vendor, | |
| item.accelerator_vendor, | |
| item.producer, | |
| item.memory.type, | |
| item.accelerators.map((accel) => accel.name).join(" "), | |
| ] | |
| .join(" ") | |
| .toLowerCase(); | |
| return ( | |
| (!search || haystack.includes(search)) && | |
| (producer === "all" || | |
| (item.accelerator_vendor || item.producer) === producer) && | |
| (band === "all" || item.latest_price_band === band) | |
| ); | |
| }); | |
| if (!rows.length) { | |
| host.innerHTML = `<div class="empty">No devices match the current filters.</div>`; | |
| return; | |
| } | |
| host.innerHTML = ` | |
| <table class="device-table"> | |
| <thead> | |
| <tr> | |
| <th>Device</th> | |
| <th>System vendor</th> | |
| <th>Accelerator vendor</th> | |
| <th>Memory</th> | |
| <th>Bandwidth</th> | |
| <th>Latest price</th> | |
| <th>Price band</th> | |
| <th>Evidence</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| ${rows | |
| .map((item) => { | |
| const event = currentPriceEvent(item); | |
| return `<tr> | |
| <td data-label="Device"><a class="device-link" href="${deviceHref(item.id)}"><strong>${escapeHTML(item.name)}</strong></a><br><span class="pill">${escapeHTML(item.item_kind.replaceAll("_", " "))}</span></td> | |
| <td data-label="System vendor">${systemVendorLink(item.system_vendor || item.producer, item.system_vendor_slug || "")}</td> | |
| <td data-label="Accelerator vendor">${acceleratorVendorLink(item.accelerator_vendor || item.producer)}</td> | |
| <td data-label="Memory">${fmtGB(item.memory.capacity_gb)} <span class="pill">${escapeHTML(item.memory.symbol)}</span><br>${escapeHTML(item.memory.type)}</td> | |
| <td data-label="Bandwidth">${fmtBandwidth(item.memory.bandwidth_gbps)}</td> | |
| <td data-label="Latest price">${fmtMoney(event.price_usd)}${estimateMark(event)}</td> | |
| <td data-label="Price band">${escapeHTML(DATA.bands.find((entry) => entry.key === event.price_band)?.label || event.price_band)}</td> | |
| <td data-label="Evidence">${escapeHTML(event.evidence)}<br><span class="pill">${escapeHTML(event.confidence)}</span></td> | |
| </tr>`; | |
| }) | |
| .join("")} | |
| </tbody> | |
| </table> | |
| `; | |
| } | |
| function hardwareDatabaseRow(item) { | |
| const event = currentPriceEvent(item); | |
| const acceleratorVendor = item.accelerator_vendor || item.producer; | |
| const systemVendor = item.system_vendor || item.producer; | |
| return { | |
| id: item.id, | |
| name: item.name, | |
| kind: item.item_kind.replaceAll("_", " "), | |
| systemVendor, | |
| systemVendorHref: systemVendorHref(item.system_vendor_slug || ""), | |
| acceleratorVendor, | |
| acceleratorVendorHref: acceleratorVendorHref( | |
| String(acceleratorVendor).toLowerCase(), | |
| ), | |
| acceleratorVendorKey: acceleratorVendor, | |
| memory: fmtGB(item.memory.capacity_gb), | |
| memorySymbol: item.memory.symbol, | |
| memoryType: item.memory.type, | |
| bandwidth: fmtBandwidth(item.memory.bandwidth_gbps), | |
| latestPrice: fmtMoney(event.price_usd), | |
| estimated: isEstimatedPrice(event), | |
| priceBand: event.price_band, | |
| priceBandLabel: | |
| DATA.bands.find((entry) => entry.key === event.price_band)?.label || | |
| event.price_band, | |
| evidence: event.evidence, | |
| confidence: event.confidence, | |
| deviceHref: deviceHref(item.id), | |
| searchText: [ | |
| item.name, | |
| item.system_vendor, | |
| item.accelerator_vendor, | |
| item.producer, | |
| item.memory.type, | |
| item.accelerators.map((accel) => accel.name).join(" "), | |
| ] | |
| .join(" ") | |
| .toLowerCase(), | |
| }; | |
| } | |
| function installHardwareBridge() { | |
| window.LocalFrontierHardwareBridge = { | |
| getState() { | |
| return hardwareBridgeState(); | |
| }, | |
| }; | |
| window.dispatchEvent(new CustomEvent("local-frontier:hardware-ready")); | |
| } | |
| function homeBridgeState() { | |
| return { | |
| stats: homeStats(), | |
| metrics: homeMetricOptions(), | |
| bands: DATA.bands.map((band) => ({ value: band.key, label: band.label })), | |
| producers, | |
| hardware: hardwareBridgeState(), | |
| }; | |
| } | |
| function installHomeBridge() { | |
| window.LocalFrontierHomeBridge = { | |
| getState() { | |
| return homeBridgeState(); | |
| }, | |
| renderChart(host, selection) { | |
| renderChart( | |
| host, | |
| metricConfig(selection.metric, selection.band, selection.producers), | |
| ); | |
| }, | |
| }; | |
| window.dispatchEvent(new CustomEvent("local-frontier:home-ready")); | |
| } | |
| function detailLink(label, href) { | |
| return { label, href }; | |
| } | |
| function deviceSourceRows(item) { | |
| const sources = []; | |
| for (const source of item.spec_sources || []) { | |
| sources.push({ label: source.name || source.url, href: source.url }); | |
| } | |
| if (item.memory.bandwidth_source_url) { | |
| sources.push({ | |
| label: "Memory bandwidth source", | |
| href: item.memory.bandwidth_source_url, | |
| }); | |
| } | |
| for (const event of item.price_history) { | |
| sources.push({ | |
| label: `${event.start_year}-${event.end_year} price source`, | |
| href: event.source_url, | |
| }); | |
| } | |
| const seen = new Set(); | |
| return sources.filter((source) => { | |
| if (!source.href || seen.has(source.href)) return false; | |
| seen.add(source.href); | |
| return true; | |
| }); | |
| } | |
| function deviceDetailState(item) { | |
| const event = currentPriceEvent(item); | |
| const acceleratorVendor = item.accelerator_vendor || item.producer; | |
| const systemVendor = item.system_vendor || item.producer; | |
| const acceleratorRows = item.accelerators.length | |
| ? item.accelerators.map((accel) => { | |
| const count = accel.count && accel.count > 1 ? `${accel.count}x ` : ""; | |
| return detailLink( | |
| `${count}${accel.vendor} ${accel.name}`, | |
| acceleratorHref(accel), | |
| ); | |
| }) | |
| : "Not specified separately"; | |
| return { | |
| specRows: [ | |
| { | |
| label: "System vendor", | |
| value: detailLink( | |
| systemVendor, | |
| systemVendorHref(item.system_vendor_slug || ""), | |
| ), | |
| }, | |
| { | |
| label: "Accelerator vendor", | |
| value: detailLink( | |
| acceleratorVendor, | |
| acceleratorVendorHref(String(acceleratorVendor).toLowerCase()), | |
| ), | |
| }, | |
| { | |
| label: "Memory", | |
| value: `${fmtGB(item.memory.capacity_gb)} ${item.memory.symbol} - ${item.memory.type}`, | |
| }, | |
| { label: "Bandwidth", value: fmtBandwidth(item.memory.bandwidth_gbps) }, | |
| { label: "Accelerators", value: acceleratorRows }, | |
| { label: "Kind", value: item.item_kind.replaceAll("_", " ") }, | |
| ], | |
| priceRows: item.price_history.map((priceEvent) => ({ | |
| years: `${priceEvent.start_year}-${priceEvent.end_year}`, | |
| price: fmtMoney(priceEvent.price_usd), | |
| evidence: priceEvent.evidence, | |
| confidence: priceEvent.confidence, | |
| })), | |
| catalogRows: item.catalog_rows.map((row) => ({ | |
| years: `${row.start_year}-${row.end_year}`, | |
| price: fmtMoney(row.price_usd), | |
| pricePerGb: fmtUsdPerGB(row.price_per_memory_gb), | |
| })), | |
| sources: deviceSourceRows(item), | |
| notes: [ | |
| event.note || "No additional note.", | |
| ...(item.memory.bandwidth_note ? [item.memory.bandwidth_note] : []), | |
| ], | |
| }; | |
| } | |
| function installDeviceBridge(item) { | |
| window.LocalFrontierDeviceBridge = { | |
| getState() { | |
| return deviceDetailState(item); | |
| }, | |
| }; | |
| window.dispatchEvent(new CustomEvent("local-frontier:device-ready")); | |
| } | |
| function renderDevicePage() { | |
| const id = window.LOCAL_FRONTIER_DEVICE_ID; | |
| const item = byId.get(id); | |
| if (!item) return; | |
| const event = currentPriceEvent(item); | |
| const itemAccelerators = item.accelerators.length | |
| ? item.accelerators | |
| .map((accel) => { | |
| const count = | |
| accel.count && accel.count > 1 ? `${accel.count}x ` : ""; | |
| return `${count}<a href="${acceleratorHref(accel, "../../")}">${escapeHTML(accel.vendor)} ${escapeHTML(accel.name)}</a>`; | |
| }) | |
| .join("<br>") | |
| : "Not specified separately"; | |
| const host = $("#devicePage"); | |
| host.innerHTML = ` | |
| <div id="deviceReactRoot"></div> | |
| <div class="device-legacy"> | |
| <section class="detail-block"> | |
| <div class="detail-card"> | |
| <h2>Specification</h2> | |
| <dl class="detail-list"> | |
| <div><dt>System vendor</dt><dd>${systemVendorLink(item.system_vendor || item.producer, item.system_vendor_slug || "", "../../")}</dd></div> | |
| <div><dt>Accelerator vendor</dt><dd>${acceleratorVendorLink(item.accelerator_vendor || item.producer, "../../")}</dd></div> | |
| <div><dt>Memory</dt><dd>${fmtGB(item.memory.capacity_gb)} ${escapeHTML(item.memory.symbol)} - ${escapeHTML(item.memory.type)}</dd></div> | |
| <div><dt>Bandwidth</dt><dd>${fmtBandwidth(item.memory.bandwidth_gbps)}</dd></div> | |
| <div><dt>Accelerators</dt><dd>${itemAccelerators}</dd></div> | |
| <div><dt>Kind</dt><dd>${escapeHTML(item.item_kind.replaceAll("_", " "))}</dd></div> | |
| </dl> | |
| </div> | |
| <div class="detail-card"> | |
| <h2>Price History</h2> | |
| <div class="table-wrap">${priceHistoryTable(item)}</div> | |
| </div> | |
| <div class="detail-card"> | |
| <h2>Catalog Rows</h2> | |
| <div class="table-wrap">${catalogRowsTable(item.catalog_rows)}</div> | |
| </div> | |
| </section> | |
| <aside class="detail-block"> | |
| <div class="detail-card"> | |
| <h2>Sources</h2> | |
| ${sourceList(item)} | |
| </div> | |
| <div class="detail-card"> | |
| <h2>Notes</h2> | |
| <p class="prose">${escapeHTML(event.note || "No additional note.")}</p> | |
| ${item.memory.bandwidth_note ? `<p class="prose">${escapeHTML(item.memory.bandwidth_note)}</p>` : ""} | |
| </div> | |
| </aside> | |
| </div> | |
| `; | |
| installDeviceBridge(item); | |
| } | |
| function acceleratorRowState(accelerator) { | |
| return { | |
| id: accelerator.id, | |
| name: accelerator.name, | |
| href: acceleratorHref(accelerator), | |
| vendor: detailLink( | |
| accelerator.vendor, | |
| acceleratorVendorHref(accelerator.vendor_slug), | |
| ), | |
| systems: String(accelerator.systems.length), | |
| maxMemory: fmtGB(accelerator.max_memory_gb), | |
| maxBandwidth: fmtBandwidth(accelerator.max_bandwidth_gbps), | |
| lowestLatestPrice: fmtMoney(accelerator.min_latest_price_usd), | |
| }; | |
| } | |
| function acceleratorSystemRowState(system) { | |
| return { | |
| id: system.item_id, | |
| name: system.name, | |
| href: deviceHref(system.item_id), | |
| kind: system.item_kind.replaceAll("_", " "), | |
| systemVendor: detailLink( | |
| system.system_vendor, | |
| systemVendorHref(system.system_vendor_slug), | |
| ), | |
| count: String(system.count || 1), | |
| memory: fmtGB(system.memory_gb), | |
| memoryType: system.memory_type, | |
| bandwidth: fmtBandwidth(system.bandwidth_gbps), | |
| latestPrice: fmtMoney(system.latest_price_usd), | |
| latestYear: String(system.latest_year), | |
| estimated: Boolean(system.latest_price_estimated), | |
| }; | |
| } | |
| function acceleratorPageState(vendorSlug, modelSlug) { | |
| if (vendorSlug && modelSlug) { | |
| const accelerator = acceleratorById.get(`${vendorSlug}/${modelSlug}`); | |
| if (!accelerator) { | |
| return { | |
| kind: "model", | |
| summaryRows: [], | |
| vendors: [], | |
| accelerators: [], | |
| systems: [], | |
| emptyMessage: "No accelerator found for this route.", | |
| }; | |
| } | |
| return { | |
| kind: "model", | |
| summaryRows: [ | |
| { | |
| label: "Accelerator vendor", | |
| value: detailLink( | |
| accelerator.vendor, | |
| acceleratorVendorHref(accelerator.vendor_slug), | |
| ), | |
| }, | |
| { label: "Systems", value: String(accelerator.systems.length) }, | |
| { label: "Max memory", value: fmtGB(accelerator.max_memory_gb) }, | |
| { | |
| label: "Max bandwidth", | |
| value: fmtBandwidth(accelerator.max_bandwidth_gbps), | |
| }, | |
| { | |
| label: "Lowest latest price", | |
| value: fmtMoney(accelerator.min_latest_price_usd), | |
| }, | |
| ], | |
| vendors: [], | |
| accelerators: [], | |
| systems: accelerator.systems.map(acceleratorSystemRowState), | |
| }; | |
| } | |
| if (vendorSlug) { | |
| const rows = accelerators.filter( | |
| (accelerator) => accelerator.vendor_slug === vendorSlug, | |
| ); | |
| if (!rows.length) { | |
| return { | |
| kind: "vendor", | |
| summaryRows: [], | |
| vendors: [], | |
| accelerators: [], | |
| systems: [], | |
| emptyMessage: "No accelerators found for this vendor.", | |
| }; | |
| } | |
| return { | |
| kind: "vendor", | |
| summaryRows: [], | |
| vendors: [], | |
| accelerators: rows.map(acceleratorRowState), | |
| systems: rows.flatMap((accelerator) => | |
| accelerator.systems.map(acceleratorSystemRowState), | |
| ), | |
| }; | |
| } | |
| const vendors = producers | |
| .map((vendor) => { | |
| const slug = vendor.toLowerCase(); | |
| const rows = accelerators.filter( | |
| (accelerator) => accelerator.vendor_slug === slug, | |
| ); | |
| const systems = rows.reduce( | |
| (total, accelerator) => total + accelerator.systems.length, | |
| 0, | |
| ); | |
| return { | |
| vendor: detailLink(vendor, acceleratorVendorHref(slug)), | |
| accelerators: String(rows.length), | |
| systems: String(systems), | |
| rows, | |
| }; | |
| }) | |
| .filter((entry) => entry.rows.length); | |
| return { | |
| kind: "overview", | |
| summaryRows: [], | |
| vendors: vendors.map(({ rows, ...entry }) => entry), | |
| accelerators: accelerators.map(acceleratorRowState), | |
| systems: [], | |
| }; | |
| } | |
| function installAcceleratorBridge(state) { | |
| window.LocalFrontierAcceleratorBridge = { | |
| getState() { | |
| return state; | |
| }, | |
| }; | |
| window.dispatchEvent(new CustomEvent("local-frontier:accelerator-ready")); | |
| } | |
| function systemVendorRowState(vendor) { | |
| return { | |
| id: vendor.slug, | |
| vendor: detailLink(vendor.name, systemVendorHref(vendor.slug)), | |
| systems: String(vendor.systems.length), | |
| acceleratorVendors: vendor.accelerator_vendors.map((name) => | |
| detailLink(name, acceleratorVendorHref(String(name).toLowerCase())), | |
| ), | |
| maxMemory: fmtGB(vendor.max_memory_gb), | |
| maxBandwidth: fmtBandwidth(vendor.max_bandwidth_gbps), | |
| lowestLatestPrice: fmtMoney(vendor.min_latest_price_usd), | |
| }; | |
| } | |
| function systemRowState(system) { | |
| return { | |
| id: system.item_id, | |
| name: system.name, | |
| href: deviceHref(system.item_id), | |
| kind: system.item_kind.replaceAll("_", " "), | |
| acceleratorVendor: detailLink( | |
| system.accelerator_vendor, | |
| acceleratorVendorHref(String(system.accelerator_vendor).toLowerCase()), | |
| ), | |
| accelerators: system.accelerators, | |
| memory: fmtGB(system.memory_gb), | |
| memoryType: system.memory_type, | |
| bandwidth: fmtBandwidth(system.bandwidth_gbps), | |
| latestPrice: fmtMoney(system.latest_price_usd), | |
| latestYear: String(system.latest_year), | |
| estimated: Boolean(system.latest_price_estimated), | |
| }; | |
| } | |
| function systemPageState(vendorSlug) { | |
| if (vendorSlug) { | |
| const vendor = systemVendorBySlug.get(vendorSlug); | |
| if (!vendor) { | |
| return { | |
| kind: "vendor", | |
| summaryRows: [], | |
| vendors: [], | |
| systems: [], | |
| emptyMessage: "No system vendor found for this route.", | |
| }; | |
| } | |
| return { | |
| kind: "vendor", | |
| summaryRows: [ | |
| { label: "Systems", value: String(vendor.systems.length) }, | |
| { | |
| label: "Accelerator vendors", | |
| value: vendor.accelerator_vendors.map((name) => | |
| detailLink(name, acceleratorVendorHref(String(name).toLowerCase())), | |
| ), | |
| }, | |
| { label: "Max memory", value: fmtGB(vendor.max_memory_gb) }, | |
| { | |
| label: "Max bandwidth", | |
| value: fmtBandwidth(vendor.max_bandwidth_gbps), | |
| }, | |
| { | |
| label: "Lowest latest price", | |
| value: fmtMoney(vendor.min_latest_price_usd), | |
| }, | |
| ], | |
| vendors: [], | |
| systems: vendor.systems.map(systemRowState), | |
| }; | |
| } | |
| return { | |
| kind: "overview", | |
| summaryRows: [], | |
| vendors: systemVendors.map(systemVendorRowState), | |
| systems: [], | |
| }; | |
| } | |
| function installSystemBridge(state) { | |
| window.LocalFrontierSystemBridge = { | |
| getState() { | |
| return state; | |
| }, | |
| }; | |
| window.dispatchEvent(new CustomEvent("local-frontier:system-ready")); | |
| } | |
| function priceHistoryTable(item) { | |
| return `<table><thead><tr><th>Years</th><th>Price</th><th>Evidence</th></tr></thead><tbody> | |
| ${item.price_history | |
| .map( | |
| (event) => `<tr> | |
| <td data-label="Years">${event.start_year}-${event.end_year}</td> | |
| <td data-label="Price">${fmtMoney(event.price_usd)}</td> | |
| <td data-label="Evidence">${escapeHTML(event.evidence)}<br><span class="pill">${escapeHTML(event.confidence)}</span></td> | |
| </tr>`, | |
| ) | |
| .join("")} | |
| </tbody></table>`; | |
| } | |
| function catalogRowsTable(rows) { | |
| if (!rows.length) | |
| return `<div class="empty">No generated catalog rows.</div>`; | |
| return `<table><thead><tr><th>Years</th><th>Price</th><th>Price/GB</th></tr></thead><tbody> | |
| ${rows | |
| .map( | |
| (row) => `<tr> | |
| <td data-label="Years">${row.start_year}-${row.end_year}</td> | |
| <td data-label="Price">${fmtMoney(row.price_usd)}</td> | |
| <td data-label="Price/GB">${fmtUsdPerGB(row.price_per_memory_gb)}</td> | |
| </tr>`, | |
| ) | |
| .join("")} | |
| </tbody></table>`; | |
| } | |
| function renderAcceleratorPage() { | |
| const host = $("#acceleratorPage"); | |
| if (!host) return; | |
| const vendorSlug = window.LOCAL_FRONTIER_ACCELERATOR_VENDOR; | |
| const modelSlug = window.LOCAL_FRONTIER_ACCELERATOR_MODEL; | |
| const state = acceleratorPageState(vendorSlug, modelSlug); | |
| host.innerHTML = ` | |
| <div id="acceleratorReactRoot"></div> | |
| <div class="accelerator-legacy"></div> | |
| `; | |
| const legacyHost = $(".accelerator-legacy", host); | |
| if (vendorSlug && modelSlug) { | |
| renderAcceleratorModelPage(legacyHost, vendorSlug, modelSlug); | |
| } else if (vendorSlug) { | |
| renderAcceleratorVendorPage(legacyHost, vendorSlug); | |
| } else { | |
| renderAcceleratorOverviewPage(legacyHost); | |
| } | |
| installAcceleratorBridge(state); | |
| } | |
| function renderSystemPage() { | |
| const host = $("#systemPage"); | |
| if (!host) return; | |
| const vendorSlug = window.LOCAL_FRONTIER_SYSTEM_VENDOR; | |
| const state = systemPageState(vendorSlug); | |
| host.innerHTML = ` | |
| <div id="systemReactRoot"></div> | |
| <div class="system-legacy"></div> | |
| `; | |
| const legacyHost = $(".system-legacy", host); | |
| if (vendorSlug) { | |
| renderSystemVendorPage(legacyHost, vendorSlug); | |
| } else { | |
| renderSystemOverviewPage(legacyHost); | |
| } | |
| installSystemBridge(state); | |
| } | |
| function renderAcceleratorOverviewPage(host) { | |
| const vendors = producers | |
| .map((vendor) => { | |
| const vendorSlug = vendor.toLowerCase(); | |
| const rows = accelerators.filter( | |
| (accelerator) => accelerator.vendor_slug === vendorSlug, | |
| ); | |
| const systems = rows.reduce( | |
| (total, accelerator) => total + accelerator.systems.length, | |
| 0, | |
| ); | |
| return { vendor, vendorSlug, rows, systems }; | |
| }) | |
| .filter((entry) => entry.rows.length); | |
| host.innerHTML = ` | |
| <section class="detail-block"> | |
| <div class="detail-card"> | |
| <h2>Vendors</h2> | |
| <div class="table-wrap"> | |
| <table> | |
| <thead><tr><th>Vendor</th><th>Accelerators</th><th>Systems</th></tr></thead> | |
| <tbody> | |
| ${vendors | |
| .map( | |
| (entry) => `<tr> | |
| <td data-label="Vendor"><a class="device-link" href="${acceleratorVendorHref(entry.vendorSlug, "../")}"><strong>${escapeHTML(entry.vendor)}</strong></a></td> | |
| <td data-label="Accelerators">${entry.rows.length}</td> | |
| <td data-label="Systems">${entry.systems}</td> | |
| </tr>`, | |
| ) | |
| .join("")} | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| <div class="detail-card"> | |
| <h2>All Accelerators</h2> | |
| ${acceleratorTable(accelerators, "../")} | |
| </div> | |
| </section> | |
| `; | |
| } | |
| function renderAcceleratorVendorPage(host, vendorSlug) { | |
| const rows = accelerators.filter( | |
| (accelerator) => accelerator.vendor_slug === vendorSlug, | |
| ); | |
| if (!rows.length) { | |
| host.innerHTML = `<div class="empty">No accelerators found for this vendor.</div>`; | |
| return; | |
| } | |
| const systems = rows.flatMap((accelerator) => accelerator.systems); | |
| host.innerHTML = ` | |
| <section class="detail-block"> | |
| <div class="detail-card"> | |
| <h2>Accelerators</h2> | |
| ${acceleratorTable(rows, "../../")} | |
| </div> | |
| <div class="detail-card"> | |
| <h2>Systems</h2> | |
| ${acceleratorSystemsTable(systems, "../../")} | |
| </div> | |
| </section> | |
| `; | |
| } | |
| function renderAcceleratorModelPage(host, vendorSlug, modelSlug) { | |
| const accelerator = acceleratorById.get(`${vendorSlug}/${modelSlug}`); | |
| if (!accelerator) { | |
| host.innerHTML = `<div class="empty">No accelerator found for this route.</div>`; | |
| return; | |
| } | |
| host.innerHTML = ` | |
| <section class="detail-block"> | |
| <div class="detail-card"> | |
| <h2>Summary</h2> | |
| <dl class="detail-list"> | |
| <div><dt>Accelerator vendor</dt><dd>${acceleratorVendorLink(accelerator.vendor, "../../../")}</dd></div> | |
| <div><dt>Systems</dt><dd>${accelerator.systems.length}</dd></div> | |
| <div><dt>Max memory</dt><dd>${fmtGB(accelerator.max_memory_gb)}</dd></div> | |
| <div><dt>Max bandwidth</dt><dd>${fmtBandwidth(accelerator.max_bandwidth_gbps)}</dd></div> | |
| <div><dt>Lowest latest price</dt><dd>${fmtMoney(accelerator.min_latest_price_usd)}</dd></div> | |
| </dl> | |
| </div> | |
| <div class="detail-card"> | |
| <h2>Systems</h2> | |
| ${acceleratorSystemsTable(accelerator.systems, "../../../")} | |
| </div> | |
| </section> | |
| `; | |
| } | |
| function acceleratorTable(rows, prefix) { | |
| if (!rows.length) return `<div class="empty">No accelerators.</div>`; | |
| return `<div class="table-wrap"><table> | |
| <thead><tr><th>Accelerator</th><th>Vendor</th><th>Systems</th><th>Max memory</th><th>Lowest latest price</th></tr></thead> | |
| <tbody> | |
| ${rows | |
| .map( | |
| (accelerator) => `<tr> | |
| <td data-label="Accelerator"><a class="device-link" href="${acceleratorHref(accelerator, prefix)}"><strong>${escapeHTML(accelerator.name)}</strong></a></td> | |
| <td data-label="Vendor">${acceleratorVendorLink(accelerator.vendor, prefix)}</td> | |
| <td data-label="Systems">${accelerator.systems.length}</td> | |
| <td data-label="Max memory">${fmtGB(accelerator.max_memory_gb)}</td> | |
| <td data-label="Lowest latest price">${fmtMoney(accelerator.min_latest_price_usd)}</td> | |
| </tr>`, | |
| ) | |
| .join("")} | |
| </tbody> | |
| </table></div>`; | |
| } | |
| function acceleratorSystemsTable(rows, prefix) { | |
| if (!rows.length) return `<div class="empty">No systems.</div>`; | |
| return `<div class="table-wrap"><table> | |
| <thead><tr><th>System</th><th>System vendor</th><th>Count</th><th>Memory</th><th>Bandwidth</th><th>Latest price</th></tr></thead> | |
| <tbody> | |
| ${rows | |
| .map( | |
| (system) => `<tr> | |
| <td data-label="System"><a class="device-link" href="${deviceHref(system.item_id, prefix)}"><strong>${escapeHTML(system.name)}</strong></a><br><span class="pill">${escapeHTML(system.item_kind.replaceAll("_", " "))}</span></td> | |
| <td data-label="System vendor">${systemVendorLink(system.system_vendor, system.system_vendor_slug, prefix)}</td> | |
| <td data-label="Count">${system.count || 1}</td> | |
| <td data-label="Memory">${fmtGB(system.memory_gb)}<br>${escapeHTML(system.memory_type)}</td> | |
| <td data-label="Bandwidth">${fmtBandwidth(system.bandwidth_gbps)}</td> | |
| <td data-label="Latest price">${fmtMoney(system.latest_price_usd)}<br><span class="pill">${escapeHTML(system.latest_year)}</span></td> | |
| </tr>`, | |
| ) | |
| .join("")} | |
| </tbody> | |
| </table></div>`; | |
| } | |
| function renderSystemOverviewPage(host) { | |
| host.innerHTML = ` | |
| <section class="detail-block"> | |
| <div class="detail-card"> | |
| <h2>System Vendors</h2> | |
| <div class="table-wrap"> | |
| <table> | |
| <thead><tr><th>System vendor</th><th>Systems</th><th>Accelerator vendors</th><th>Max memory</th><th>Lowest latest price</th></tr></thead> | |
| <tbody> | |
| ${systemVendors | |
| .map( | |
| (vendor) => `<tr> | |
| <td data-label="System vendor">${systemVendorLink(vendor.name, vendor.slug, "../")}</td> | |
| <td data-label="Systems">${vendor.systems.length}</td> | |
| <td data-label="Accelerator vendors">${vendor.accelerator_vendors.map((name) => acceleratorVendorLink(name, "../")).join("<br>")}</td> | |
| <td data-label="Max memory">${fmtGB(vendor.max_memory_gb)}</td> | |
| <td data-label="Lowest latest price">${fmtMoney(vendor.min_latest_price_usd)}</td> | |
| </tr>`, | |
| ) | |
| .join("")} | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </section> | |
| `; | |
| } | |
| function renderSystemVendorPage(host, vendorSlug) { | |
| const vendor = systemVendorBySlug.get(vendorSlug); | |
| if (!vendor) { | |
| host.innerHTML = `<div class="empty">No system vendor found for this route.</div>`; | |
| return; | |
| } | |
| host.innerHTML = ` | |
| <section class="detail-block"> | |
| <div class="detail-card"> | |
| <h2>Summary</h2> | |
| <dl class="detail-list"> | |
| <div><dt>Systems</dt><dd>${vendor.systems.length}</dd></div> | |
| <div><dt>Accelerator vendors</dt><dd>${vendor.accelerator_vendors.map((name) => acceleratorVendorLink(name, "../../")).join("<br>")}</dd></div> | |
| <div><dt>Max memory</dt><dd>${fmtGB(vendor.max_memory_gb)}</dd></div> | |
| <div><dt>Max bandwidth</dt><dd>${fmtBandwidth(vendor.max_bandwidth_gbps)}</dd></div> | |
| <div><dt>Lowest latest price</dt><dd>${fmtMoney(vendor.min_latest_price_usd)}</dd></div> | |
| </dl> | |
| </div> | |
| <div class="detail-card"> | |
| <h2>Systems</h2> | |
| ${systemVendorSystemsTable(vendor.systems, "../../")} | |
| </div> | |
| </section> | |
| `; | |
| } | |
| function systemVendorSystemsTable(rows, prefix) { | |
| if (!rows.length) return `<div class="empty">No systems.</div>`; | |
| return `<div class="table-wrap"><table> | |
| <thead><tr><th>System</th><th>Accelerator vendor</th><th>Accelerators</th><th>Memory</th><th>Bandwidth</th><th>Latest price</th></tr></thead> | |
| <tbody> | |
| ${rows | |
| .map( | |
| (system) => `<tr> | |
| <td data-label="System"><a class="device-link" href="${deviceHref(system.item_id, prefix)}"><strong>${escapeHTML(system.name)}</strong></a><br><span class="pill">${escapeHTML(system.item_kind.replaceAll("_", " "))}</span></td> | |
| <td data-label="Accelerator vendor">${acceleratorVendorLink(system.accelerator_vendor, prefix)}</td> | |
| <td data-label="Accelerators">${system.accelerators.map((name) => escapeHTML(name)).join("<br>")}</td> | |
| <td data-label="Memory">${fmtGB(system.memory_gb)}<br>${escapeHTML(system.memory_type)}</td> | |
| <td data-label="Bandwidth">${fmtBandwidth(system.bandwidth_gbps)}</td> | |
| <td data-label="Latest price">${fmtMoney(system.latest_price_usd)}${system.latest_price_estimated ? ` <span class="estimate-mark" title="Estimated or reconstructed price">≈</span>` : ""}<br><span class="pill">${escapeHTML(system.latest_year)}</span></td> | |
| </tr>`, | |
| ) | |
| .join("")} | |
| </tbody> | |
| </table></div>`; | |
| } | |
| function hardwareCandidates() { | |
| return DATA.items | |
| .filter( | |
| (item) => | |
| Number.isFinite(Number(item.memory?.capacity_gb)) && | |
| Number.isFinite(Number(item.memory?.bandwidth_gbps)), | |
| ) | |
| .sort( | |
| (a, b) => | |
| b.memory.capacity_gb - a.memory.capacity_gb || | |
| b.memory.bandwidth_gbps - a.memory.bandwidth_gbps || | |
| a.name.localeCompare(b.name), | |
| ); | |
| } | |
| function modelSourceList(model) { | |
| if (!model.sources?.length) | |
| return `<p class="empty">No model source links recorded.</p>`; | |
| return `<ul class="source-list">${model.sources | |
| .map( | |
| (source) => | |
| `<li><a href="${escapeHTML(source.url)}" rel="noreferrer">${escapeHTML(source.label || source.url)}</a></li>`, | |
| ) | |
| .join("")}</ul>`; | |
| } | |
| function modelExpertText(architecture) { | |
| if (!architecture.routed_experts) return "No routed experts"; | |
| return `${fmtNumber(architecture.routed_experts)} routed, ${fmtNumber(architecture.routed_experts_per_token)} routed/token, ${fmtNumber(architecture.shared_experts_per_token || 0)} shared/token`; | |
| } | |
| function modelAttentionText(architecture) { | |
| const parts = []; | |
| if (architecture.attention_heads) | |
| parts.push(`${fmtNumber(architecture.attention_heads)} query heads`); | |
| if (architecture.kv_heads) | |
| parts.push(`${fmtNumber(architecture.kv_heads)} KV heads`); | |
| if (architecture.global_kv_heads) | |
| parts.push(`${fmtNumber(architecture.global_kv_heads)} global KV heads`); | |
| if (architecture.head_dim) | |
| parts.push(`${fmtNumber(architecture.head_dim)} head dim`); | |
| if (architecture.state_size) | |
| parts.push(`${fmtNumber(architecture.state_size)} state size`); | |
| return parts.length ? parts.join(", ") : "Architecture-specific cache"; | |
| } | |
| function renderModelsPage() { | |
| const host = $("#modelsPage"); | |
| if (!host) return; | |
| const id = window.LOCAL_FRONTIER_MODEL_ID; | |
| if (id) { | |
| renderModelDetailPage(host, id); | |
| } else { | |
| renderModelOverviewPage(host); | |
| } | |
| } | |
| function renderModelOverviewPage(host) { | |
| if (!models.length) { | |
| host.innerHTML = `<div class="empty">No model metadata is loaded.</div>`; | |
| return; | |
| } | |
| host.innerHTML = ` | |
| <section class="detail-block"> | |
| <div id="modelsReactRoot"></div> | |
| <div class="detail-card model-legacy"> | |
| <h2>Model Database</h2> | |
| <div class="table-controls model-controls"> | |
| <label>Search | |
| <input id="modelSearch" type="search" placeholder="Qwen/Qwen3.6-35B-A3B, MoE, 32B"> | |
| </label> | |
| </div> | |
| <div id="modelTable" class="model-table-host"></div> | |
| </div> | |
| </section> | |
| `; | |
| $("#modelSearch")?.addEventListener("input", renderModelTable); | |
| renderModelTable(); | |
| installModelsBridge(); | |
| } | |
| function modelDatabaseRow(model) { | |
| return { | |
| id: model.id, | |
| name: model.name, | |
| precision: model.adapter.weight_precision, | |
| downloads: fmtNumber(model.hf_downloads || 0), | |
| architecture: model.architecture.type, | |
| architectureDetail: model.architecture.detail || "", | |
| adapterKind: model.adapter.kind, | |
| totalParams: fmtParams(model.architecture.total_params_b), | |
| activeParams: fmtParams(model.architecture.active_params_b), | |
| residentFootprint: fmtGBDecimal(model.adapter.resident_weight_gb, 2), | |
| activeTrafficFloor: fmtGBDecimal(model.adapter.active_weight_gb, 2), | |
| context: fmtTokenCount(model.architecture.max_context_tokens), | |
| modelHref: modelHref(model.id), | |
| compareHref: compareHref([model.id], ["nvidia-dgx-spark"]), | |
| searchText: [ | |
| model.id, | |
| ...(model.aliases || []), | |
| model.name, | |
| model.short_name, | |
| model.license, | |
| String(model.hf_downloads || ""), | |
| model.architecture.type, | |
| model.architecture.detail, | |
| model.adapter.kind, | |
| model.adapter.weight_precision, | |
| (model.tags || []).join(" "), | |
| ] | |
| .join(" ") | |
| .toLowerCase(), | |
| }; | |
| } | |
| function installModelsBridge() { | |
| window.LocalFrontierModelsBridge = { | |
| getState() { | |
| return { | |
| rows: models.map(modelDatabaseRow), | |
| }; | |
| }, | |
| }; | |
| window.dispatchEvent(new CustomEvent("local-frontier:models-ready")); | |
| } | |
| function modelDetailState(model) { | |
| const arch = model.architecture; | |
| const adapter = model.adapter; | |
| return { | |
| architectureRows: [ | |
| { | |
| label: "Hugging Face ID", | |
| value: detailLink(model.id, huggingFaceModelHref(model)), | |
| }, | |
| { | |
| label: "HF downloads", | |
| value: fmtNumber(model.hf_downloads || 0), | |
| }, | |
| { | |
| label: "Type", | |
| value: `${arch.type} - ${arch.detail || "No detail recorded."}`, | |
| }, | |
| { | |
| label: "Parameters", | |
| value: `${fmtParams(arch.total_params_b)} total, ${fmtParams(arch.active_params_b)} active`, | |
| }, | |
| { label: "Experts", value: modelExpertText(arch) }, | |
| { | |
| label: "Layers", | |
| value: `${fmtNumber(arch.layers)}${arch.sliding_window_tokens ? `, sliding window ${fmtNumber(arch.sliding_window_tokens)} tokens` : ""}`, | |
| }, | |
| { label: "Attention/state", value: modelAttentionText(arch) }, | |
| { | |
| label: "Max context", | |
| value: fmtTokenCount(arch.max_context_tokens), | |
| }, | |
| ], | |
| adapterRows: [ | |
| { | |
| label: "Weight precision", | |
| value: `${adapter.weight_precision} (${fmtNumber(adapter.weight_bytes_per_param, 3)} bytes/param)`, | |
| }, | |
| { | |
| label: "Resident weights", | |
| value: fmtGBDecimal(adapter.resident_weight_gb, 2), | |
| }, | |
| { | |
| label: "Active traffic floor", | |
| value: `${fmtGBDecimal(adapter.active_weight_gb, 2)} per decode iteration`, | |
| }, | |
| { | |
| label: "KV allocation", | |
| value: `${fmtGBDecimal(adapter.kv_alloc_gb_per_1k_tokens, 4)} per 1K reserved tokens/session`, | |
| }, | |
| { | |
| label: "KV read traffic", | |
| value: `${fmtGBDecimal(adapter.kv_read_gb_per_1k_tokens, 4)} per 1K active tokens/output token`, | |
| }, | |
| ], | |
| sources: (model.sources || []).map((source) => ({ | |
| label: source.label || source.url, | |
| href: source.url, | |
| })), | |
| notes: adapter.notes ? [adapter.notes] : [], | |
| compareHref: compareHref([model.id], ["nvidia-dgx-spark"]), | |
| }; | |
| } | |
| function installModelDetailBridge(model) { | |
| window.LocalFrontierModelDetailBridge = { | |
| getState() { | |
| return modelDetailState(model); | |
| }, | |
| }; | |
| window.dispatchEvent(new CustomEvent("local-frontier:model-detail-ready")); | |
| } | |
| function filteredModels() { | |
| const search = ($("#modelSearch")?.value || "").trim().toLowerCase(); | |
| return models.filter((model) => { | |
| const haystack = [ | |
| model.id, | |
| ...(model.aliases || []), | |
| model.name, | |
| model.short_name, | |
| model.license, | |
| String(model.hf_downloads || ""), | |
| model.architecture.type, | |
| model.architecture.detail, | |
| model.adapter.kind, | |
| model.adapter.weight_precision, | |
| (model.tags || []).join(" "), | |
| ] | |
| .join(" ") | |
| .toLowerCase(); | |
| return !search || haystack.includes(search); | |
| }); | |
| } | |
| function renderModelTable() { | |
| const host = $("#modelTable"); | |
| if (!host) return; | |
| const rows = filteredModels(); | |
| if (!rows.length) { | |
| host.innerHTML = `<div class="empty">No models match the current filters.</div>`; | |
| return; | |
| } | |
| host.innerHTML = ` | |
| <div class="table-wrap"> | |
| <table> | |
| <thead><tr><th>HF model</th><th>Downloads</th><th>Architecture</th><th>Parameters</th><th>Resident footprint</th><th>Context</th><th>Calculator</th></tr></thead> | |
| <tbody> | |
| ${rows | |
| .map( | |
| (model) => `<tr> | |
| <td data-label="HF model"><a class="device-link" href="${modelHref(model.id, "../")}"><strong>${escapeHTML(model.id)}</strong></a><br>${escapeHTML(model.name)}<br><span class="pill">${escapeHTML(model.adapter.weight_precision)}</span></td> | |
| <td data-label="Downloads">${fmtNumber(model.hf_downloads || 0)}</td> | |
| <td data-label="Architecture">${escapeHTML(model.architecture.type)}<br>${escapeHTML(model.architecture.detail || "")}<br><span class="pill">${escapeHTML(model.adapter.kind)}</span></td> | |
| <td data-label="Parameters">${fmtParams(model.architecture.total_params_b)} total<br>${fmtParams(model.architecture.active_params_b)} active</td> | |
| <td data-label="Resident footprint">${fmtGBDecimal(model.adapter.resident_weight_gb, 2)}<br>${fmtGBDecimal(model.adapter.active_weight_gb, 2)} active traffic floor</td> | |
| <td data-label="Context">${fmtTokenCount(model.architecture.max_context_tokens)}</td> | |
| <td data-label="Calculator"><a class="device-link" href="${compareHref([model.id], ["nvidia-dgx-spark"])}">Compare</a></td> | |
| </tr>`, | |
| ) | |
| .join("")} | |
| </tbody> | |
| </table> | |
| </div> | |
| `; | |
| } | |
| function renderModelDetailPage(host, id) { | |
| const model = modelLookup.get(id); | |
| if (!model) { | |
| host.innerHTML = `<div class="empty">No model found for this route.</div>`; | |
| return; | |
| } | |
| const arch = model.architecture; | |
| const adapter = model.adapter; | |
| host.innerHTML = ` | |
| <div id="modelDetailReactRoot"></div> | |
| <div class="model-detail-legacy"> | |
| <section class="detail-block"> | |
| <div class="detail-card"> | |
| <h2>Architecture</h2> | |
| <dl class="detail-list"> | |
| <div><dt>Hugging Face ID</dt><dd><a class="device-link" href="${huggingFaceModelHref(model)}" rel="noreferrer">${escapeHTML(model.id)}</a></dd></div> | |
| <div><dt>HF downloads</dt><dd>${fmtNumber(model.hf_downloads || 0)}</dd></div> | |
| <div><dt>Type</dt><dd>${escapeHTML(arch.type)} - ${escapeHTML(arch.detail || "No detail recorded.")}</dd></div> | |
| <div><dt>Parameters</dt><dd>${fmtParams(arch.total_params_b)} total, ${fmtParams(arch.active_params_b)} active</dd></div> | |
| <div><dt>Experts</dt><dd>${escapeHTML(modelExpertText(arch))}</dd></div> | |
| <div><dt>Layers</dt><dd>${fmtNumber(arch.layers)}${arch.sliding_window_tokens ? `, sliding window ${fmtNumber(arch.sliding_window_tokens)} tokens` : ""}</dd></div> | |
| <div><dt>Attention/state</dt><dd>${escapeHTML(modelAttentionText(arch))}</dd></div> | |
| <div><dt>Max context</dt><dd>${fmtTokenCount(arch.max_context_tokens)}</dd></div> | |
| </dl> | |
| </div> | |
| <div class="detail-card"> | |
| <h2>Bound Adapter</h2> | |
| <dl class="detail-list"> | |
| <div><dt>Weight precision</dt><dd>${escapeHTML(adapter.weight_precision)} (${fmtNumber(adapter.weight_bytes_per_param, 3)} bytes/param)</dd></div> | |
| <div><dt>Resident weights</dt><dd>${fmtGBDecimal(adapter.resident_weight_gb, 2)}</dd></div> | |
| <div><dt>Active traffic floor</dt><dd>${fmtGBDecimal(adapter.active_weight_gb, 2)} per decode iteration</dd></div> | |
| <div><dt>KV allocation</dt><dd>${fmtGBDecimal(adapter.kv_alloc_gb_per_1k_tokens, 4)} per 1K reserved tokens/session</dd></div> | |
| <div><dt>KV read traffic</dt><dd>${fmtGBDecimal(adapter.kv_read_gb_per_1k_tokens, 4)} per 1K active tokens/output token</dd></div> | |
| </dl> | |
| ${adapter.notes ? `<p class="prose model-note">${escapeHTML(adapter.notes)}</p>` : ""} | |
| </div> | |
| <div class="detail-card"> | |
| <h2>Sources</h2> | |
| ${modelSourceList(model)} | |
| </div> | |
| <div class="detail-card"> | |
| <h2>Calculator</h2> | |
| <p class="prose">Use this model with any hardware row in the local frontier catalog.</p> | |
| <p><a class="action-link" href="${compareHref([model.id], ["nvidia-dgx-spark"])}">Compare</a></p> | |
| </div> | |
| </section> | |
| </div> | |
| `; | |
| installModelDetailBridge(model); | |
| } | |
| function idsFromParams(params, names) { | |
| return idList(names.flatMap((name) => params.getAll(name))); | |
| } | |
| function validIds(ids, lookup) { | |
| return idList(ids).filter((id) => lookup.has(id)); | |
| } | |
| function validModelIds(ids) { | |
| const canonical = []; | |
| for (const id of idList(ids)) { | |
| const model = modelLookup.get(id); | |
| if (model && !canonical.includes(model.id)) canonical.push(model.id); | |
| } | |
| return canonical; | |
| } | |
| function boundsRouteSelection(parts) { | |
| if (parts[0] !== "bounds") return { modelIds: [], hardwareIds: [] }; | |
| const onIndex = parts.indexOf("on"); | |
| if (onIndex > 1) { | |
| return { | |
| modelIds: [parts.slice(1, onIndex).join("/")], | |
| hardwareIds: parts[onIndex + 1] ? [parts[onIndex + 1]] : [], | |
| }; | |
| } | |
| return { | |
| modelIds: parts[1] ? [parts.slice(1).join("/")] : [], | |
| hardwareIds: [], | |
| }; | |
| } | |
| function defaultHardwareId(hardwareRows) { | |
| return byId.has("nvidia-dgx-spark") | |
| ? "nvidia-dgx-spark" | |
| : hardwareRows[0]?.id; | |
| } | |
| function compareSelectionFromUrl(hardwareRows) { | |
| const params = new URLSearchParams(window.location.search); | |
| const parts = routeParts(); | |
| const routeSelection = boundsRouteSelection(parts); | |
| const requestedModels = [ | |
| ...routeSelection.modelIds, | |
| ...idsFromParams(params, ["models", "model"]), | |
| ]; | |
| const requestedHardware = [ | |
| ...routeSelection.hardwareIds, | |
| ...idsFromParams(params, ["hardware", "hardwares"]), | |
| ]; | |
| const modelIds = validModelIds(requestedModels); | |
| const hardwareIds = validIds(requestedHardware, byId).filter((id) => | |
| hardwareRows.some((item) => item.id === id), | |
| ); | |
| return { | |
| modelIds: modelIds.length ? modelIds : [models[0].id], | |
| hardwareIds: hardwareIds.length | |
| ? hardwareIds | |
| : [defaultHardwareId(hardwareRows)].filter(Boolean), | |
| }; | |
| } | |
| function modelSearchText(model) { | |
| return [ | |
| model.id, | |
| model.name, | |
| model.short_name, | |
| model.family, | |
| model.provider, | |
| model.architecture?.kind, | |
| ...(model.aliases || []), | |
| ] | |
| .join(" ") | |
| .toLowerCase(); | |
| } | |
| function hardwareSearchText(item) { | |
| return [ | |
| item.id, | |
| item.name, | |
| item.system_vendor, | |
| item.accelerator_vendor, | |
| item.memory?.capacity_gb, | |
| item.memory?.bandwidth_gbps, | |
| ...(item.accelerators || []).map((accelerator) => accelerator.name), | |
| ] | |
| .join(" ") | |
| .toLowerCase(); | |
| } | |
| function fuzzyRank(query, text) { | |
| const q = query.trim().toLowerCase(); | |
| if (!q) return 0; | |
| if (text.includes(q)) return 1000 - text.indexOf(q); | |
| let score = 0; | |
| let cursor = 0; | |
| for (const char of q) { | |
| const next = text.indexOf(char, cursor); | |
| if (next === -1) return -1; | |
| score += Math.max(1, 20 - (next - cursor)); | |
| cursor = next + 1; | |
| } | |
| return score; | |
| } | |
| function popularModelIds() { | |
| return [ | |
| "nvidia/Qwen3.6-35B-A3B-NVFP4", | |
| "nvidia/Gemma-4-26B-A4B-NVFP4", | |
| "antirez/deepseek-v4-gguf", | |
| "Qwen/Qwen3.6-35B-A3B", | |
| "google/gemma-4-26B-A4B-it", | |
| "deepseek-ai/DeepSeek-V4-Flash", | |
| "Qwen/Qwen3-32B", | |
| "Qwen/Qwen3-8B", | |
| "mistralai/Mixtral-8x7B-Instruct-v0.1", | |
| ].filter((id) => modelById.has(id)); | |
| } | |
| function popularHardwareIds(hardwareRows) { | |
| return [ | |
| "nvidia-dgx-spark", | |
| "apple-macbook-pro-16-m5-max-128gb", | |
| "apple-mac-studio-m3-ultra-current-96gb-configuration", | |
| "apple-mac-studio-m3-ultra-512gb-launch-configuration", | |
| "nvidia-dgx-station-a100-320g", | |
| "amd-framework-desktop-ryzen-ai-max-plus-395-128gb", | |
| ].filter((id) => hardwareRows.some((item) => item.id === id)); | |
| } | |
| function compareOptionLabel(kind, item) { | |
| if (kind === "model") return item.id; | |
| return item.id; | |
| } | |
| function compareOptionMeta(kind, item) { | |
| if (kind === "model") { | |
| return [ | |
| item.short_name || item.name, | |
| item.architecture?.total_params_b | |
| ? `${fmtParams(item.architecture.total_params_b)} total` | |
| : "", | |
| item.adapter?.weight_precision, | |
| item.hf_downloads ? `${fmtNumber(item.hf_downloads)} downloads` : "", | |
| ] | |
| .filter(Boolean) | |
| .join(" · "); | |
| } | |
| return [ | |
| item.name, | |
| `${fmtGB(item.memory.capacity_gb)}, ${fmtBandwidth(item.memory.bandwidth_gbps)}`, | |
| item.system_vendor || item.producer, | |
| item.accelerator_vendor || item.producer, | |
| ] | |
| .filter(Boolean) | |
| .join(" · "); | |
| } | |
| function compareAssumptionControls(assumptions) { | |
| return [ | |
| { | |
| id: "boundLAlloc", | |
| label: "Reserved context", | |
| min: "1", | |
| step: "1000", | |
| value: String(Number(assumptions.default_l_alloc_tokens || 100000)), | |
| }, | |
| { | |
| id: "boundLRead", | |
| label: "Read context", | |
| min: "1", | |
| step: "1000", | |
| value: String(Number(assumptions.default_l_read_tokens || 32000)), | |
| }, | |
| { | |
| id: "boundOverhead", | |
| label: "Overhead GB", | |
| min: "0", | |
| step: "1", | |
| value: String(Number(assumptions.default_overhead_gb || 8)), | |
| }, | |
| { | |
| id: "boundRStar", | |
| label: "Min tok/s per session", | |
| min: "0", | |
| step: "1", | |
| value: String(Number(assumptions.default_r_star_toks || 20)), | |
| }, | |
| ]; | |
| } | |
| function compareSuggestions(kind, items, query, selectedIds, popularIds) { | |
| const selected = new Set(selectedIds); | |
| const searchText = kind === "model" ? modelSearchText : hardwareSearchText; | |
| const ranked = items | |
| .filter((item) => !selected.has(item.id)) | |
| .map((item) => ({ item, score: fuzzyRank(query, searchText(item)) })) | |
| .filter((entry) => entry.score >= 0); | |
| if (query.trim()) { | |
| return ranked | |
| .sort((a, b) => b.score - a.score) | |
| .slice(0, 8) | |
| .map((entry) => entry.item); | |
| } | |
| const popular = popularIds | |
| .map((id) => items.find((item) => item.id === id)) | |
| .filter((item) => item && !selected.has(item.id)); | |
| const fallback = items.filter( | |
| (item) => | |
| !selected.has(item.id) && !popular.some((entry) => entry.id === item.id), | |
| ); | |
| return [...popular, ...fallback].slice(0, 8); | |
| } | |
| function selectedCompareIds(kind) { | |
| return [ | |
| ...document.querySelectorAll(`.compare-check[data-kind="${kind}"]:checked`), | |
| ].map((input) => input.value); | |
| } | |
| function allListedCompareIds(kind) { | |
| return [ | |
| ...document.querySelectorAll(`.compare-check[data-kind="${kind}"]`), | |
| ].map((input) => input.value); | |
| } | |
| function renderCompareSelectedList(kind, items, selectedIds) { | |
| const itemById = new Map(items.map((item) => [item.id, item])); | |
| const rows = idList(selectedIds) | |
| .map((id) => itemById.get(id)) | |
| .filter(Boolean); | |
| if (!rows.length) { | |
| return `<div class="compare-empty">No ${kind === "model" ? "models" : "hardware"} selected.</div>`; | |
| } | |
| return `<div class="compare-selected-list"> | |
| ${rows | |
| .map( | |
| (item) => `<div class="compare-selected-row"> | |
| <label class="compare-selected-check"> | |
| <input class="compare-check" type="checkbox" data-kind="${kind}" value="${escapeHTML(item.id)}" checked> | |
| <span> | |
| <strong>${escapeHTML(compareOptionLabel(kind, item))}</strong> | |
| <small>${escapeHTML(compareOptionMeta(kind, item))}</small> | |
| </span> | |
| </label> | |
| <button class="compare-remove" type="button" data-kind="${kind}" data-id="${escapeHTML(item.id)}" aria-label="Remove ${escapeHTML(compareOptionLabel(kind, item))}">×</button> | |
| </div>`, | |
| ) | |
| .join("")} | |
| </div>`; | |
| } | |
| function setupComparePicker({ kind, inputId, listId, items, popularIds }) { | |
| const input = $(`#${inputId}`); | |
| const list = $(`#${listId}`); | |
| if (!input || !list) return; | |
| const selectedIds = () => allListedCompareIds(kind); | |
| const renderSuggestions = () => { | |
| hideCompareSuggestions(list); | |
| const suggestions = compareSuggestions( | |
| kind, | |
| items, | |
| input.value, | |
| selectedIds(), | |
| popularIds, | |
| ); | |
| list.innerHTML = suggestions.length | |
| ? suggestions | |
| .map( | |
| ( | |
| item, | |
| index, | |
| ) => `<button class="compare-suggestion${index === 0 ? " is-active" : ""}" type="button" data-kind="${kind}" data-id="${escapeHTML(item.id)}"> | |
| <strong>${escapeHTML(compareOptionLabel(kind, item))}</strong> | |
| <span>${escapeHTML(compareOptionMeta(kind, item))}</span> | |
| </button>`, | |
| ) | |
| .join("") | |
| : `<div class="compare-no-suggestions">No matches</div>`; | |
| list.hidden = false; | |
| }; | |
| input.addEventListener("focus", renderSuggestions); | |
| input.addEventListener("input", renderSuggestions); | |
| input.addEventListener("keydown", (event) => { | |
| if (event.key === "Escape") { | |
| hideCompareSuggestions(); | |
| input.blur(); | |
| return; | |
| } | |
| if (event.key !== "Enter") return; | |
| event.preventDefault(); | |
| const first = list.querySelector(".compare-suggestion"); | |
| if (first) addCompareItem(first.dataset.kind, first.dataset.id); | |
| }); | |
| } | |
| function hideCompareSuggestions(exceptList) { | |
| document.querySelectorAll(".compare-suggestions").forEach((list) => { | |
| if (list !== exceptList) list.hidden = true; | |
| }); | |
| } | |
| function handleCompareOutsidePointerDown(event) { | |
| const target = | |
| event.target instanceof Element | |
| ? event.target | |
| : event.target?.parentElement; | |
| if (!target?.closest(".compare-picker-wrap")) hideCompareSuggestions(); | |
| } | |
| function setCompareList(kind, ids) { | |
| const items = kind === "model" ? models : hardwareCandidates(); | |
| const host = $(`#${kind}SelectedList`); | |
| if (!host) return; | |
| host.innerHTML = renderCompareSelectedList(kind, items, ids); | |
| host.querySelectorAll(".compare-check").forEach((input) => { | |
| input.addEventListener("change", renderCompareResult); | |
| }); | |
| host.querySelectorAll(".compare-remove").forEach((button) => { | |
| button.addEventListener("click", () => | |
| removeCompareItem(button.dataset.kind, button.dataset.id), | |
| ); | |
| }); | |
| } | |
| function addCompareItem(kind, id) { | |
| const existing = allListedCompareIds(kind); | |
| if (!existing.includes(id)) existing.push(id); | |
| setCompareList(kind, existing); | |
| const input = $(`#${kind}Picker`); | |
| if (input) input.value = ""; | |
| hideCompareSuggestions(); | |
| renderCompareResult(); | |
| input?.focus(); | |
| } | |
| function removeCompareItem(kind, id) { | |
| setCompareList( | |
| kind, | |
| allListedCompareIds(kind).filter((itemId) => itemId !== id), | |
| ); | |
| renderCompareResult(); | |
| } | |
| function renderComparePage() { | |
| const host = $("#comparePage"); | |
| if (!host) return; | |
| if (!models.length) { | |
| host.innerHTML = `<div class="empty">No model metadata is loaded.</div>`; | |
| return; | |
| } | |
| const hardwareRows = hardwareCandidates(); | |
| const selection = compareSelectionFromUrl(hardwareRows); | |
| const assumptions = MODEL_DATA.assumptions || {}; | |
| const assumptionControls = compareAssumptionControls(assumptions); | |
| host.innerHTML = ` | |
| <section class="detail-block"> | |
| <div class="compare-builder"> | |
| <div id="compareReactRoot"></div> | |
| <div class="compare-vector" aria-label="Hardware by model selection"> | |
| <div class="compare-column"> | |
| <h3>Hardware</h3> | |
| <div class="compare-picker-wrap"> | |
| <label>Find hardware | |
| <input id="hardwarePicker" class="compare-picker" type="search" autocomplete="off" placeholder="Select hardware"> | |
| </label> | |
| <div id="hardwareSuggestionList" class="compare-suggestions" hidden></div> | |
| </div> | |
| <div id="hardwareSelectedList"></div> | |
| </div> | |
| <div class="compare-times" aria-hidden="true">×</div> | |
| <div class="compare-column"> | |
| <h3>Models</h3> | |
| <div class="compare-picker-wrap"> | |
| <label>Find model | |
| <input id="modelPicker" class="compare-picker" type="search" autocomplete="off" placeholder="Select model"> | |
| </label> | |
| <div id="modelSuggestionList" class="compare-suggestions" hidden></div> | |
| </div> | |
| <div id="modelSelectedList"></div> | |
| </div> | |
| </div> | |
| <div class="bound-controls compare-assumptions"> | |
| ${assumptionControls | |
| .map( | |
| (control) => `<label>${escapeHTML(control.label)} | |
| <input id="${escapeHTML(control.id)}" type="number" min="${escapeHTML(control.min)}" step="${escapeHTML(control.step)}" value="${escapeHTML(control.value)}"> | |
| </label>`, | |
| ) | |
| .join("")} | |
| </div> | |
| </div> | |
| <div id="compareResultReactRoot"></div> | |
| <div id="boundsResult" class="bounds-result"></div> | |
| </section> | |
| `; | |
| setCompareList("hardware", selection.hardwareIds); | |
| setCompareList("model", selection.modelIds); | |
| setupComparePicker({ | |
| kind: "hardware", | |
| inputId: "hardwarePicker", | |
| listId: "hardwareSuggestionList", | |
| items: hardwareRows, | |
| popularIds: popularHardwareIds(hardwareRows), | |
| }); | |
| setupComparePicker({ | |
| kind: "model", | |
| inputId: "modelPicker", | |
| listId: "modelSuggestionList", | |
| items: models, | |
| popularIds: popularModelIds(), | |
| }); | |
| document.removeEventListener( | |
| "pointerdown", | |
| handleCompareOutsidePointerDown, | |
| true, | |
| ); | |
| document.addEventListener( | |
| "pointerdown", | |
| handleCompareOutsidePointerDown, | |
| true, | |
| ); | |
| document.querySelectorAll(".compare-suggestions").forEach((list) => { | |
| list.addEventListener("click", (event) => { | |
| const button = event.target.closest(".compare-suggestion"); | |
| if (button) addCompareItem(button.dataset.kind, button.dataset.id); | |
| }); | |
| }); | |
| [ | |
| "boundLAlloc", | |
| "boundLRead", | |
| "boundOverhead", | |
| "boundRStar", | |
| ].forEach((id) => { | |
| const el = $(`#${id}`); | |
| if (el) { | |
| el.addEventListener("input", renderCompareResult); | |
| el.addEventListener("change", renderCompareResult); | |
| } | |
| }); | |
| renderCompareResult(); | |
| installCompareBridge(hardwareRows, models, assumptionControls); | |
| } | |
| function installCompareBridge(hardwareRows, modelRows, assumptionControls) { | |
| window.LocalFrontierCompareBridge = { | |
| getState() { | |
| return { | |
| hardwareOptions: hardwareRows.map((item) => ({ | |
| id: item.id, | |
| label: compareOptionLabel("hardware", item), | |
| meta: compareOptionMeta("hardware", item), | |
| })), | |
| modelOptions: modelRows.map((model) => ({ | |
| id: model.id, | |
| label: compareOptionLabel("model", model), | |
| meta: compareOptionMeta("model", model), | |
| })), | |
| hardwareIds: allListedCompareIds("hardware"), | |
| modelIds: allListedCompareIds("model"), | |
| assumptions: assumptionControls.map((control) => ({ | |
| ...control, | |
| value: $(`#${control.id}`)?.value || control.value, | |
| })), | |
| resultOptions: compareResultOptionsSnapshot(), | |
| result: | |
| currentCompareResultSnapshot || | |
| compareResultSnapshot({ canonicalPath: "/compare" }), | |
| }; | |
| }, | |
| setSelectedIds(kind, ids) { | |
| setCompareList(kind, idList(ids)); | |
| renderCompareResult(); | |
| }, | |
| setAssumptionValue(id, value) { | |
| const input = $(`#${id}`); | |
| if (input) { | |
| input.value = value; | |
| renderCompareResult(); | |
| } | |
| }, | |
| setResultOption(id, value) { | |
| setCompareResultOption(id, value); | |
| }, | |
| }; | |
| window.dispatchEvent(new CustomEvent("local-frontier:compare-ready")); | |
| } | |
| function numberFromInput(id, fallback) { | |
| const value = Number($(`#${id}`)?.value); | |
| return Number.isFinite(value) ? value : fallback; | |
| } | |
| function selectedValues(id) { | |
| return Array.from($(`#${id}`)?.selectedOptions || []).map( | |
| (option) => option.value, | |
| ); | |
| } | |
| function boundWarnings(model, result, lAlloc, lRead) { | |
| const warnings = [...(result.warnings || [])]; | |
| const maxContext = recordedContextLimit(model); | |
| if (maxContext !== null && lAlloc > maxContext) { | |
| warnings.push( | |
| `Reserved context exceeds this model's recorded max context of ${fmtNumber(maxContext)} tokens.`, | |
| ); | |
| } | |
| if (maxContext !== null && lRead > maxContext) { | |
| warnings.push( | |
| `Read context exceeds this model's recorded max context of ${fmtNumber(maxContext)} tokens.`, | |
| ); | |
| } | |
| if (lRead > lAlloc) { | |
| warnings.push("Read context is greater than reserved context."); | |
| } | |
| if (result.status.residentFit.code === "unsupported_profile") { | |
| warnings.push("No audited production bounds are available for this model profile."); | |
| } else if (result.status.residentFit.code === "resident_not_fit") { | |
| warnings.push( | |
| "Resident model plus overhead does not fit in selected hardware memory.", | |
| ); | |
| } else if (result.status.sessionFit.code === "no_session_capacity") { | |
| warnings.push("No active session fits after reserved context allocation."); | |
| } | |
| return warnings; | |
| } | |
| function compareWarnings(rows, lAlloc, lRead) { | |
| const warnings = rows.flatMap(({ result }) => result.warnings || []); | |
| const contextModels = new Set(); | |
| for (const { model } of rows) { | |
| const maxContext = recordedContextLimit(model); | |
| if ( | |
| maxContext !== null && | |
| (lAlloc > maxContext || lRead > maxContext) | |
| ) { | |
| contextModels.add(model.short_name || model.name); | |
| } | |
| } | |
| if (contextModels.size) { | |
| warnings.push( | |
| `Context settings exceed recorded max context for ${[...contextModels].join(", ")}.`, | |
| ); | |
| } | |
| if (lRead > lAlloc) { | |
| warnings.push("Read context is greater than reserved context."); | |
| } | |
| const noFit = rows.filter( | |
| ({ result }) => result.status.residentFit.code === "resident_not_fit", | |
| ).length; | |
| const unsupported = rows.filter( | |
| ({ result }) => result.status.residentFit.code === "unsupported_profile", | |
| ).length; | |
| const noSessions = rows.filter( | |
| ({ result }) => result.status.sessionFit.code === "no_session_capacity", | |
| ).length; | |
| if (unsupported) | |
| warnings.push( | |
| `${fmtNumber(unsupported)} combinations have no audited production model profile.`, | |
| ); | |
| if (noFit) | |
| warnings.push( | |
| `${fmtNumber(noFit)} combinations do not fit resident model weights plus overhead.`, | |
| ); | |
| if (noSessions) | |
| warnings.push( | |
| `${fmtNumber(noSessions)} combinations fit weights but no active session after reserved context allocation.`, | |
| ); | |
| return warnings; | |
| } | |
| function memoryFitLabel(result) { | |
| if (result.status.residentFit.code === "unsupported_profile") { | |
| return "No audited profile"; | |
| } | |
| switch (result.status.sessionFit.code) { | |
| case "fits": | |
| return `${fmtNumber(result.status.sessionFit.sessionCount)} sessions`; | |
| case "no_session_capacity": | |
| return "No active session"; | |
| case "not_evaluated": | |
| return "Weights do not fit"; | |
| default: | |
| return "Unknown"; | |
| } | |
| } | |
| function aggregateLabel(result) { | |
| if (result.status.residentFit.code === "unsupported_profile") { | |
| return "No profile"; | |
| } | |
| switch (result.status.floorFit.code) { | |
| case "meets_floor": | |
| return fmtTok(result.best.aggregate, 0); | |
| case "below_floor": | |
| return "No floor"; | |
| case "not_evaluated": | |
| return "No fit"; | |
| default: | |
| return "-"; | |
| } | |
| } | |
| function usableBatchLabel(result) { | |
| return result.status.floorFit.ok ? fmtNumber(result.best.batch) : "-"; | |
| } | |
| function perSessionLabel(result) { | |
| return result.status.floorFit.ok ? fmtTok(result.best.perSession, 1) : "-"; | |
| } | |
| function compareMatrixStatus(result) { | |
| if ( | |
| result.status.residentFit.code === "unsupported_profile" || | |
| result.status.residentFit.code === "resident_not_fit" || | |
| result.status.sessionFit.code === "no_session_capacity" | |
| ) { | |
| return "bad"; | |
| } | |
| if (result.status.floorFit.code === "below_floor") return "warn"; | |
| if (result.status.floorFit.ok) return "ok"; | |
| return "neutral"; | |
| } | |
| function compareTextValue(label, value) { | |
| return { label, value }; | |
| } | |
| function compareLinkValue(label, href, valueLabel, sublabel = "") { | |
| return { | |
| label, | |
| value: { | |
| href, | |
| label: valueLabel, | |
| ...(sublabel ? { sublabel } : {}), | |
| }, | |
| }; | |
| } | |
| function compareCell(value, subvalue = "") { | |
| return { | |
| value, | |
| ...(subvalue ? { subvalue } : {}), | |
| }; | |
| } | |
| function compareLinkCell(href, label, subvalue = "") { | |
| return compareCell( | |
| { | |
| href, | |
| label, | |
| }, | |
| subvalue, | |
| ); | |
| } | |
| function compareResultOptionsSnapshot() { | |
| return { ...compareResultOptions }; | |
| } | |
| function setCompareResultOption(id, value) { | |
| if (id === "groupBy") { | |
| compareResultOptions.groupBy = value === "hardware" ? "hardware" : "model"; | |
| } | |
| if (id === "advanced") { | |
| compareResultOptions.advanced = Boolean(value); | |
| } | |
| renderCompareResult(); | |
| } | |
| function compareFitLabel(result) { | |
| switch (result.status_code) { | |
| case "ok": | |
| return "Fits"; | |
| case "unsupported_profile": | |
| return "No profile"; | |
| case "resident_not_fit": | |
| return "Weights do not fit"; | |
| case "no_session_capacity": | |
| return "No active session"; | |
| case "no_floor": | |
| return "Below floor"; | |
| default: | |
| return result.status_code || "Unknown"; | |
| } | |
| } | |
| function compareHasAuditedProfile(result) { | |
| return result.status_code !== "unsupported_profile"; | |
| } | |
| function compareHasRunnableSession(result) { | |
| return ( | |
| compareHasAuditedProfile(result) && | |
| result.status.residentFit.code !== "resident_not_fit" && | |
| result.status.sessionFit.code !== "no_session_capacity" | |
| ); | |
| } | |
| function compareNumberValue(value, formatter, fallback = "-") { | |
| return Number.isFinite(Number(value)) ? formatter(value) : fallback; | |
| } | |
| function compareSingleSessionLabel(result) { | |
| return compareHasRunnableSession(result) | |
| ? compareNumberValue(result.single?.aggregate, (value) => fmtTok(value, 1)) | |
| : "-"; | |
| } | |
| function compareMemoryFitBatchLabel(result) { | |
| return compareHasAuditedProfile(result) | |
| ? compareNumberValue(result.bMem, (value) => fmtNumber(value)) | |
| : "-"; | |
| } | |
| function compareMemoryPowerCeilingLabel(result) { | |
| return compareHasAuditedProfile(result) | |
| ? compareNumberValue(result.memoryPowerCeiling, (value) => fmtTok(value, 0)) | |
| : "-"; | |
| } | |
| function compareTraceGB(result, key, digits = 3) { | |
| return compareNumberValue(result.trace?.[key], (value) => | |
| fmtGBDecimal(value, digits), | |
| ); | |
| } | |
| function compareTraceTok(result, key, digits = 1) { | |
| return compareNumberValue(result.trace?.[key], (value) => | |
| fmtTok(value, digits), | |
| ); | |
| } | |
| function compareInputValue(workloadSettings, key, fallback = "-") { | |
| const value = workloadSettings?.[key]; | |
| return value === null || value === undefined || value === "" ? fallback : value; | |
| } | |
| function compareAdapterKind(profile, adapterKey) { | |
| return profile?.architecture?.[adapterKey]?.kind || "-"; | |
| } | |
| function comparePrecisionLabel(profile) { | |
| if (!profile?.serving) return "-"; | |
| return [ | |
| profile.serving.weight_format ? `W ${profile.serving.weight_format}` : "", | |
| profile.serving.kv_store_format | |
| ? `KV ${profile.serving.kv_store_format}` | |
| : "", | |
| profile.serving.kv_read_format | |
| ? `read ${profile.serving.kv_read_format}` | |
| : "", | |
| ] | |
| .filter(Boolean) | |
| .join(" / "); | |
| } | |
| function compareUsableRange(result) { | |
| const summary = result.trace?.usable_batches_summary; | |
| if (!summary?.count) return "-"; | |
| if (summary.min_batch === summary.max_batch) { | |
| return fmtNumber(summary.min_batch); | |
| } | |
| return `${fmtNumber(summary.min_batch)}-${fmtNumber(summary.max_batch)}`; | |
| } | |
| function compareSubjectCell(groupBy, model, hardware) { | |
| if (groupBy === "hardware") { | |
| return compareLinkCell( | |
| modelHref(model.id), | |
| model.short_name || model.name, | |
| model.id, | |
| ); | |
| } | |
| return compareLinkCell( | |
| deviceHref(hardware.id), | |
| hardware.name, | |
| `${fmtGB(hardware.memory.capacity_gb)} at ${fmtBandwidth(hardware.memory.bandwidth_gbps)}`, | |
| ); | |
| } | |
| function compareDefaultColumns(groupBy) { | |
| return [ | |
| { | |
| key: "subject", | |
| label: groupBy === "hardware" ? "Model" : "Hardware", | |
| }, | |
| { key: "fit", label: "Fit" }, | |
| { key: "singleSession", label: "Single-session tok/s" }, | |
| { key: "memoryFitBatch", label: "Max fit sessions" }, | |
| { key: "bStar", label: "Usable sessions b*" }, | |
| { key: "aggregate", label: "Aggregate tok/s at b*" }, | |
| { key: "perSession", label: "Per-session tok/s at b*" }, | |
| { key: "memoryPowerCeiling", label: "Memory-power bound" }, | |
| ]; | |
| } | |
| function compareAdvancedColumns() { | |
| return [ | |
| { key: "profile", label: "Profile" }, | |
| { key: "statusCode", label: "Status code" }, | |
| { key: "capacity", label: "Capacity" }, | |
| { key: "bandwidth", label: "Bandwidth" }, | |
| { key: "memoryPower", label: "Memory power" }, | |
| { key: "resident", label: "Resident GB" }, | |
| { key: "free", label: "Free GB" }, | |
| { key: "kAlloc", label: "K alloc/session" }, | |
| { key: "wBatchOne", label: "W batch(1)" }, | |
| { key: "wBatchBStar", label: "W batch(b*)" }, | |
| { key: "kRead", label: "K read/token" }, | |
| { key: "qBStar", label: "Q at b*" }, | |
| { key: "usableRange", label: "Usable b range" }, | |
| { key: "inputs", label: "Inputs" }, | |
| { key: "weightAdapter", label: "Weight adapter" }, | |
| { key: "kvAdapter", label: "KV adapter" }, | |
| { key: "precision", label: "Precision" }, | |
| { key: "warnings", label: "Warnings" }, | |
| ]; | |
| } | |
| function compareTableColumns(groupBy, advanced) { | |
| return [ | |
| ...compareDefaultColumns(groupBy), | |
| ...(advanced ? compareAdvancedColumns() : []), | |
| ]; | |
| } | |
| function compareResultTableRow(row, groupBy, workloadSettings) { | |
| const { model, hardware, profile, result } = row; | |
| const usableBatch = result.status.floorFit.ok ? result.best?.batch : null; | |
| const aggregate = result.status.floorFit.ok ? result.best?.aggregate : null; | |
| const perSession = result.status.floorFit.ok ? result.best?.perSession : null; | |
| const bStarLabel = | |
| usableBatch === null || usableBatch === undefined | |
| ? "-" | |
| : fmtNumber(usableBatch); | |
| const aggregateLabel = | |
| aggregate === null || aggregate === undefined ? "-" : fmtTok(aggregate, 0); | |
| const perSessionValue = | |
| perSession === null || perSession === undefined | |
| ? "-" | |
| : fmtTok(perSession, 1); | |
| const warnings = result.warnings?.length | |
| ? result.warnings.join("; ") | |
| : result.status_code === "ok" | |
| ? "-" | |
| : compareFitLabel(result); | |
| return { | |
| subject: compareSubjectCell(groupBy, model, hardware), | |
| fit: compareCell(compareFitLabel(result), result.status_code), | |
| singleSession: compareCell(compareSingleSessionLabel(result)), | |
| memoryFitBatch: compareCell(compareMemoryFitBatchLabel(result)), | |
| bStar: compareCell(bStarLabel), | |
| aggregate: compareCell(aggregateLabel), | |
| perSession: compareCell(perSessionValue), | |
| memoryPowerCeiling: compareCell(compareMemoryPowerCeilingLabel(result)), | |
| profile: compareCell( | |
| result.profile_resolution?.model_profile || "-", | |
| result.profile_resolution?.model_profile_status || "", | |
| ), | |
| statusCode: compareCell(result.status_code || "-"), | |
| capacity: compareCell(compareTraceGB(result, "capacity_gb", 0)), | |
| bandwidth: compareCell(compareNumberValue(result.trace?.bandwidth_gbps, fmtBandwidth)), | |
| memoryPower: compareCell( | |
| compareNumberValue(result.memoryPower, (value) => | |
| `${fmtNumber(value, 0)} GB^2/s`, | |
| ), | |
| ), | |
| resident: compareCell(compareTraceGB(result, "w_resident_gb", 3)), | |
| free: compareCell(compareTraceGB(result, "free_gb", 3)), | |
| kAlloc: compareCell(compareTraceGB(result, "k_alloc_gb", 4)), | |
| wBatchOne: compareCell( | |
| compareNumberValue(result.single?.batchWeight, (value) => | |
| fmtGBDecimal(value, 3), | |
| ), | |
| ), | |
| wBatchBStar: compareCell(compareTraceGB(result, "w_batch_at_b_star_gb", 3)), | |
| kRead: compareCell(compareTraceGB(result, "k_read_gb", 4)), | |
| qBStar: compareCell(compareTraceGB(result, "q_at_b_star_gb_per_output_token", 4)), | |
| usableRange: compareCell(compareUsableRange(result)), | |
| inputs: compareCell( | |
| `${fmtNumber(compareInputValue(workloadSettings, "l_alloc_tokens"))}/${fmtNumber(compareInputValue(workloadSettings, "l_read_tokens"))} ctx`, | |
| `floor ${fmtTok(compareInputValue(workloadSettings, "min_toks_per_session"), 1)}, overhead ${fmtGBDecimal(compareInputValue(workloadSettings, "overhead_gb"), 1)}`, | |
| ), | |
| weightAdapter: compareCell(compareAdapterKind(profile, "weight_adapter")), | |
| kvAdapter: compareCell(compareAdapterKind(profile, "kv_adapter")), | |
| precision: compareCell(comparePrecisionLabel(profile)), | |
| warnings: compareCell(warnings), | |
| }; | |
| } | |
| function compareGroupedSections({ | |
| rows, | |
| selectedModels, | |
| selectedHardware, | |
| groupBy, | |
| advanced, | |
| workloadSettings, | |
| }) { | |
| const columns = compareTableColumns(groupBy, advanced); | |
| const rowByKey = new Map( | |
| rows.map((row) => [`${row.model.id}\n${row.hardware.id}`, row]), | |
| ); | |
| const minWidth = advanced ? "3000px" : "1160px"; | |
| if (groupBy === "hardware") { | |
| return selectedHardware.map((hardware) => ({ | |
| kind: "table", | |
| title: hardware.name, | |
| description: `${fmtGB(hardware.memory.capacity_gb)} at ${fmtBandwidth(hardware.memory.bandwidth_gbps)}`, | |
| minWidth, | |
| columns, | |
| rows: selectedModels | |
| .map((model) => rowByKey.get(`${model.id}\n${hardware.id}`)) | |
| .filter(Boolean) | |
| .map((row) => compareResultTableRow(row, groupBy, workloadSettings)), | |
| })); | |
| } | |
| return selectedModels.map((model) => ({ | |
| kind: "table", | |
| title: model.short_name || model.name, | |
| description: model.id, | |
| minWidth, | |
| columns, | |
| rows: selectedHardware | |
| .map((hardware) => rowByKey.get(`${model.id}\n${hardware.id}`)) | |
| .filter(Boolean) | |
| .map((row) => compareResultTableRow(row, groupBy, workloadSettings)), | |
| })); | |
| } | |
| function comparePairDetailSection(row, workloadSettings) { | |
| const { model, hardware, profile, result } = row; | |
| return { | |
| kind: "list", | |
| title: "Calculation Details", | |
| description: | |
| "KV-aware bound: T(b) <= R / (W_batch(b) / b + K_read). The loose memory-power bound keeps C x R explicit and drops private read traffic.", | |
| rows: [ | |
| compareLinkValue("Model", modelHref(model.id), model.id, model.name), | |
| compareLinkValue("Hardware", deviceHref(hardware.id), hardware.name), | |
| compareTextValue( | |
| "Hardware memory", | |
| `${fmtGB(hardware.memory.capacity_gb)} at ${fmtBandwidth(hardware.memory.bandwidth_gbps)}`, | |
| ), | |
| compareTextValue( | |
| "Profile", | |
| result.profile_resolution?.model_profile || "-", | |
| ), | |
| compareTextValue( | |
| "Profile status", | |
| result.profile_resolution?.model_profile_status || "-", | |
| ), | |
| compareTextValue( | |
| "Weight adapter", | |
| compareAdapterKind(profile, "weight_adapter"), | |
| ), | |
| compareTextValue("KV adapter", compareAdapterKind(profile, "kv_adapter")), | |
| compareTextValue("Precision", comparePrecisionLabel(profile)), | |
| compareTextValue( | |
| "Reserved / read context", | |
| `${fmtNumber(compareInputValue(workloadSettings, "l_alloc_tokens"))} / ${fmtNumber(compareInputValue(workloadSettings, "l_read_tokens"))} tokens`, | |
| ), | |
| compareTextValue( | |
| "Floor", | |
| fmtTok(compareInputValue(workloadSettings, "min_toks_per_session"), 1), | |
| ), | |
| compareTextValue( | |
| "Resident model", | |
| compareTraceGB(result, "w_resident_gb", 3), | |
| ), | |
| compareTextValue( | |
| "Free after overhead", | |
| compareTraceGB(result, "free_gb", 3), | |
| ), | |
| compareTextValue( | |
| "K_alloc per session", | |
| compareTraceGB(result, "k_alloc_gb", 4), | |
| ), | |
| compareTextValue( | |
| "K_read per output token", | |
| compareTraceGB(result, "k_read_gb", 4), | |
| ), | |
| compareTextValue( | |
| "W_batch(1)", | |
| compareNumberValue(result.single?.batchWeight, (value) => | |
| fmtGBDecimal(value, 3), | |
| ), | |
| ), | |
| compareTextValue( | |
| "W_batch(b*)", | |
| compareTraceGB(result, "w_batch_at_b_star_gb", 3), | |
| ), | |
| compareTextValue( | |
| "Q at b*", | |
| compareTraceGB(result, "q_at_b_star_gb_per_output_token", 4), | |
| ), | |
| compareTextValue("Usable b range", compareUsableRange(result)), | |
| ], | |
| }; | |
| } | |
| function compareResultSnapshot({ | |
| canonicalPath, | |
| warnings = [], | |
| cards = [], | |
| sections = [], | |
| emptyMessage = "", | |
| }) { | |
| return { | |
| canonicalPath, | |
| canonicalUrl: `${window.location.origin}${canonicalPath}`, | |
| warnings, | |
| cards, | |
| sections, | |
| ...(emptyMessage ? { emptyMessage } : {}), | |
| }; | |
| } | |
| function publishCompareResultSnapshot(snapshot) { | |
| currentCompareResultSnapshot = snapshot; | |
| window.dispatchEvent(new CustomEvent("local-frontier:compare-result")); | |
| } | |
| function renderCompareResult() { | |
| const host = $("#boundsResult"); | |
| if (!host) return; | |
| const selectedModels = selectedCompareIds("model") | |
| .map((id) => modelById.get(id)) | |
| .filter(Boolean); | |
| const selectedHardware = selectedCompareIds("hardware") | |
| .map((id) => byId.get(id)) | |
| .filter(Boolean); | |
| const canonicalPath = compareHref( | |
| selectedModels.map((model) => model.id), | |
| selectedHardware.map((item) => item.id), | |
| ); | |
| if ( | |
| `${window.location.pathname}${window.location.search}` !== canonicalPath && | |
| window.history?.replaceState | |
| ) { | |
| window.history.replaceState(null, "", canonicalPath); | |
| } | |
| if (!selectedModels.length || !selectedHardware.length) { | |
| publishCompareResultSnapshot( | |
| compareResultSnapshot({ | |
| canonicalPath, | |
| emptyMessage: "Choose at least one model and one hardware row.", | |
| }), | |
| ); | |
| host.innerHTML = ` | |
| <div class="empty">Choose at least one model and one hardware row.</div> | |
| `; | |
| return; | |
| } | |
| const lAlloc = numberFromInput("boundLAlloc", 100000); | |
| const lRead = numberFromInput("boundLRead", 32000); | |
| const overhead = numberFromInput("boundOverhead", 8); | |
| const rStar = numberFromInput("boundRStar", 20); | |
| const workloadSettings = { | |
| l_alloc_tokens: lAlloc, | |
| l_read_tokens: lRead, | |
| min_toks_per_session: rStar, | |
| overhead_gb: overhead, | |
| decode_policy: { | |
| kind: "ordinary", | |
| rho: 1, | |
| }, | |
| }; | |
| const rows = selectedModels.flatMap((model) => | |
| selectedHardware.map((hardware) => { | |
| const profile = profileByRepo.get(model.id); | |
| const result = BOUNDS_ENGINE.calculateProfiledBounds({ | |
| profile, | |
| model, | |
| hardware, | |
| workloadSettings, | |
| }); | |
| return { model, hardware, profile, result }; | |
| }), | |
| ); | |
| const isPair = selectedModels.length === 1 && selectedHardware.length === 1; | |
| const warnings = isPair | |
| ? boundWarnings(rows[0].model, rows[0].result, lAlloc, lRead) | |
| : compareWarnings(rows, lAlloc, lRead); | |
| const resultOptions = compareResultOptionsSnapshot(); | |
| const sections = compareGroupedSections({ | |
| rows, | |
| selectedModels, | |
| selectedHardware, | |
| groupBy: resultOptions.groupBy, | |
| advanced: resultOptions.advanced, | |
| workloadSettings, | |
| }); | |
| if (isPair) sections.push(comparePairDetailSection(rows[0], workloadSettings)); | |
| publishCompareResultSnapshot( | |
| compareResultSnapshot({ | |
| canonicalPath, | |
| warnings, | |
| sections, | |
| }), | |
| ); | |
| host.innerHTML = ""; | |
| } | |
| function sourceList(item) { | |
| const sources = []; | |
| for (const source of item.spec_sources || []) { | |
| sources.push({ name: source.name, url: source.url }); | |
| } | |
| if (item.memory.bandwidth_source_url) { | |
| sources.push({ | |
| name: "Memory bandwidth source", | |
| url: item.memory.bandwidth_source_url, | |
| }); | |
| } | |
| for (const event of item.price_history) { | |
| sources.push({ | |
| name: `${event.start_year}-${event.end_year} price source`, | |
| url: event.source_url, | |
| }); | |
| } | |
| const unique = []; | |
| const seen = new Set(); | |
| for (const source of sources) { | |
| if (!source.url || seen.has(source.url)) continue; | |
| seen.add(source.url); | |
| unique.push(source); | |
| } | |
| if (!unique.length) return `<p class="empty">No source links recorded.</p>`; | |
| return `<ul class="source-list">${unique.map((source) => `<li><a href="${escapeHTML(source.url)}" rel="noreferrer">${escapeHTML(source.name || source.url)}</a></li>`).join("")}</ul>`; | |
| } | |
| const HOME_SHELL_HTML = document.querySelector(".site-shell")?.outerHTML || ""; | |
| let currentCompareResultSnapshot = null; | |
| const DEFAULT_COMPARE_RESULT_OPTIONS = Object.freeze({ | |
| groupBy: "model", | |
| advanced: false, | |
| }); | |
| let compareResultOptions = { ...DEFAULT_COMPARE_RESULT_OPTIONS }; | |
| function primaryNav() { | |
| return `<nav class="top-nav" aria-label="Primary"> | |
| <a class="wordmark" href="/">Local Frontier</a> | |
| <div class="nav-links"> | |
| <a href="/#frontier">Charts</a> | |
| <a href="/#database">Database</a> | |
| <a href="/accelerators">Accelerators</a> | |
| <a href="/systems">Systems</a> | |
| <a href="/models">Models</a> | |
| <a href="/compare">Compare</a> | |
| </div> | |
| </nav>`; | |
| } | |
| function renderShell({ title, mainId, backHref = "", backLabel = "" }) { | |
| const backLink = backHref | |
| ? `<a class="back-link" href="${backHref}">← ${escapeHTML(backLabel)}</a>` | |
| : ""; | |
| document.body.innerHTML = `<div class="site-shell device-shell"> | |
| <header class="device-header"> | |
| ${primaryNav()} | |
| ${backLink} | |
| <h1>${escapeHTML(title)}</h1> | |
| </header> | |
| <main id="${mainId}" class="device-main"></main> | |
| </div>`; | |
| } | |
| function renderHomeRoute() { | |
| document.body.innerHTML = ` | |
| <div id="homeReactRoot"></div> | |
| <div class="home-legacy">${HOME_SHELL_HTML}</div> | |
| `; | |
| document.body.dataset.page = "index"; | |
| document.title = "Local Frontier"; | |
| renderStats(); | |
| setupControls(); | |
| renderFrontierChart(); | |
| renderDeviceTable(); | |
| installHardwareBridge(); | |
| installHomeBridge(); | |
| if (window.location.hash) { | |
| requestAnimationFrame(() => { | |
| document.querySelector(window.location.hash)?.scrollIntoView(); | |
| }); | |
| } | |
| } | |
| function routeParts() { | |
| return window.location.pathname | |
| .replace(/^\/+|\/+$/g, "") | |
| .split("/") | |
| .filter(Boolean) | |
| .map((part) => decodeURIComponent(part)); | |
| } | |
| function clearRouteGlobals() { | |
| delete document.body.dataset.reactAccelerator; | |
| delete document.body.dataset.reactCompare; | |
| delete document.body.dataset.reactCompareResult; | |
| delete document.body.dataset.reactModels; | |
| delete document.body.dataset.reactModelDetail; | |
| delete document.body.dataset.reactHardware; | |
| delete document.body.dataset.reactDevice; | |
| delete document.body.dataset.reactHome; | |
| delete document.body.dataset.reactSystem; | |
| window.LOCAL_FRONTIER_DEVICE_ID = undefined; | |
| window.LOCAL_FRONTIER_ACCELERATOR_VENDOR = undefined; | |
| window.LOCAL_FRONTIER_ACCELERATOR_MODEL = undefined; | |
| window.LOCAL_FRONTIER_SYSTEM_VENDOR = undefined; | |
| window.LOCAL_FRONTIER_MODEL_ID = undefined; | |
| } | |
| function renderRoute() { | |
| clearRouteGlobals(); | |
| const parts = routeParts(); | |
| if (parts[0] === "devices" && parts[1]) { | |
| const item = byId.get(parts[1]); | |
| document.body.dataset.page = "device"; | |
| document.title = `${item?.name || "Device"} - Local Frontier`; | |
| window.LOCAL_FRONTIER_DEVICE_ID = parts[1]; | |
| renderShell({ | |
| title: item?.name || "Device", | |
| mainId: "devicePage", | |
| backHref: "/#database", | |
| backLabel: "Back to database", | |
| }); | |
| renderDevicePage(); | |
| return; | |
| } | |
| if (parts[0] === "accelerators") { | |
| const vendorSlug = parts[1]; | |
| const modelSlug = parts[2]; | |
| const accelerator = modelSlug | |
| ? acceleratorById.get(`${vendorSlug}/${modelSlug}`) | |
| : null; | |
| const vendor = vendorSlug | |
| ? producers.find((name) => name.toLowerCase() === vendorSlug) | |
| : null; | |
| document.body.dataset.page = modelSlug | |
| ? "accelerator-model" | |
| : vendorSlug | |
| ? "accelerator-vendor" | |
| : "accelerators"; | |
| window.LOCAL_FRONTIER_ACCELERATOR_VENDOR = vendorSlug; | |
| window.LOCAL_FRONTIER_ACCELERATOR_MODEL = modelSlug; | |
| const title = | |
| accelerator?.name || (vendor ? `${vendor} Accelerators` : "Accelerators"); | |
| document.title = `${title} - Local Frontier`; | |
| renderShell({ | |
| title, | |
| mainId: "acceleratorPage", | |
| backHref: vendorSlug ? "/accelerators" : "", | |
| backLabel: "Back to accelerators", | |
| }); | |
| renderAcceleratorPage(); | |
| return; | |
| } | |
| if (parts[0] === "systems") { | |
| const vendorSlug = parts[1]; | |
| const vendor = vendorSlug ? systemVendorBySlug.get(vendorSlug) : null; | |
| document.body.dataset.page = vendorSlug ? "system-vendor" : "systems"; | |
| window.LOCAL_FRONTIER_SYSTEM_VENDOR = vendorSlug; | |
| const title = vendor ? `${vendor.name} Systems` : "System Vendors"; | |
| document.title = `${title} - Local Frontier`; | |
| renderShell({ | |
| title, | |
| mainId: "systemPage", | |
| backHref: vendorSlug ? "/systems" : "", | |
| backLabel: "Back to systems", | |
| }); | |
| renderSystemPage(); | |
| return; | |
| } | |
| if (parts[0] === "models") { | |
| const requestedModelId = parts.length > 1 ? parts.slice(1).join("/") : ""; | |
| const model = requestedModelId ? modelLookup.get(requestedModelId) : null; | |
| const canonicalModelId = model?.id || requestedModelId; | |
| document.body.dataset.page = requestedModelId ? "model" : "models"; | |
| window.LOCAL_FRONTIER_MODEL_ID = canonicalModelId; | |
| const title = model ? model.name : "Models"; | |
| document.title = `${title} - Local Frontier`; | |
| renderShell({ | |
| title, | |
| mainId: "modelsPage", | |
| backHref: requestedModelId ? "/models" : "", | |
| backLabel: "Back to models", | |
| }); | |
| renderModelsPage(); | |
| if ( | |
| model && | |
| requestedModelId !== model.id && | |
| window.history?.replaceState | |
| ) { | |
| window.history.replaceState(null, "", modelHref(model.id)); | |
| } | |
| return; | |
| } | |
| if (parts[0] === "compare" || parts[0] === "bounds") { | |
| document.body.dataset.page = "compare"; | |
| document.title = "Model / Hardware Compare - Local Frontier"; | |
| renderShell({ | |
| title: "Model / Hardware Compare", | |
| mainId: "comparePage", | |
| backHref: "/models", | |
| backLabel: "Back to models", | |
| }); | |
| renderComparePage(); | |
| return; | |
| } | |
| renderHomeRoute(); | |
| } | |
| function installRouteInterception() { | |
| document.addEventListener("click", (event) => { | |
| const link = event.target.closest("a[href]"); | |
| if (!link) return; | |
| const url = new URL(link.href, window.location.href); | |
| if (url.origin !== window.location.origin) return; | |
| event.preventDefault(); | |
| history.pushState(null, "", `${url.pathname}${url.search}${url.hash}`); | |
| renderRoute(); | |
| }); | |
| window.addEventListener("popstate", renderRoute); | |
| } | |
| function init() { | |
| installRouteInterception(); | |
| renderRoute(); | |
| } | |
| init(); | |