osolmaz/ourmodels-data / scripts /build_static_space.py
osolmaz's picture
download
raw
47.9 kB
from __future__ import annotations
import csv
import html
import json
import shutil
from pathlib import Path
from typing import Any
ROOT = Path(__file__).resolve().parents[1]
DATA_DIR = ROOT / "data"
ITEMS_DIR = DATA_DIR / "items"
SPACE_DIR = ROOT / "space"
ASSETS_DIR = SPACE_DIR / "assets"
DEVICES_DIR = SPACE_DIR / "devices"
CSV_FILES = {
"annual": DATA_DIR / "annual_frontier_2020_2026.csv",
"dominant": DATA_DIR / "dominant_frontier_2020_2026.csv",
"best_price": DATA_DIR / "best_price_per_memory_2020_2026.csv",
"best_bandwidth": DATA_DIR / "best_bandwidth_2020_2026.csv",
"projection": DATA_DIR / "consumer_memory_linear_projection_2020_2030.csv",
"catalog": DATA_DIR / "verified_system_catalog.csv",
"memory_price": DATA_DIR / "memory_price_per_gb.csv",
}
BAND_ORDER = [
"under_3000",
"3000_5000",
"5000_10000",
"10000_25000",
"25000_75000",
"75000_plus",
]
BAND_LABELS = {
"under_3000": "Under $3,000",
"3000_5000": "$3,000-$5,000",
"5000_10000": "$5,000-$10,000",
"10000_25000": "$10,000-$25,000",
"25000_75000": "$25,000-$75,000",
"75000_plus": "$75,000+",
}
PRODUCER_ORDER = ["Apple", "NVIDIA", "AMD"]
def read_csv(path: Path) -> list[dict[str, str]]:
with path.open(newline="", encoding="utf-8") as f:
return list(csv.DictReader(f))
def read_items() -> list[dict[str, Any]]:
return [
json.loads(path.read_text(encoding="utf-8"))
for path in sorted(ITEMS_DIR.glob("*.json"))
]
def as_number(value: Any) -> int | float | None:
if value in ("", None):
return None
if isinstance(value, (int, float)):
return value
text = str(value)
try:
parsed = float(text)
except ValueError:
return None
if parsed.is_integer():
return int(parsed)
return parsed
def normalize_row(row: dict[str, str]) -> dict[str, Any]:
normalized: dict[str, Any] = {}
for key, value in row.items():
if key in {
"year",
"start_year",
"end_year",
"price_usd",
"memory_gb",
"bandwidth_gbps",
"frontier_set_year",
"fit_base_year",
}:
normalized[key] = as_number(value)
elif key in {
"metric_value",
"price_per_memory_gb",
"linear_fit_memory_gb",
"observed_best_so_far_memory_gb",
"slope_gb_per_year",
"fit_base_memory_gb",
}:
normalized[key] = as_number(value)
elif key in {"carried_forward", "is_projection"}:
normalized[key] = value == "True"
else:
normalized[key] = value
return normalized
def item_price_summary(item: dict[str, Any]) -> dict[str, Any]:
history = sorted(
item["price_history"],
key=lambda event: (event["end_year"], event["start_year"], event["catalog_order"]),
)
latest = history[-1]
prices = [event["price_usd"] for event in history]
return {
"latest_price_usd": latest["price_usd"],
"latest_price_band": latest["price_band"],
"latest_price_band_label": BAND_LABELS[latest["price_band"]],
"latest_price_kind": latest["price_kind"],
"latest_year": latest["end_year"],
"min_price_usd": min(prices),
"max_price_usd": max(prices),
"price_event_count": len(history),
}
def build_payload() -> dict[str, Any]:
raw_items = read_items()
csv_payload = {
name: [normalize_row(row) for row in read_csv(path)]
for name, path in CSV_FILES.items()
}
catalog_by_item: dict[str, list[dict[str, Any]]] = {}
for row in csv_payload["catalog"]:
catalog_by_item.setdefault(row["item_id"], []).append(row)
items: list[dict[str, Any]] = []
for item in raw_items:
price_summary = item_price_summary(item)
catalog_rows = sorted(
catalog_by_item.get(item["id"], []),
key=lambda row: (row.get("start_year") or 0, row.get("price_usd") or 0),
)
items.append({
"id": item["id"],
"producer": item["producer"],
"name": item["name"],
"item_kind": item["item_kind"],
"memory": item["memory"],
"accelerators": item.get("accelerators", []),
"spec_sources": item.get("spec_sources", []),
"price_history": item["price_history"],
"catalog_rows": catalog_rows,
**price_summary,
})
items.sort(key=lambda item: (
PRODUCER_ORDER.index(item["producer"]),
-(item["memory"]["capacity_gb"]),
item["name"],
))
latest_catalog = [
row for row in csv_payload["catalog"]
if row.get("end_year") == 2026
]
max_memory = max(item["memory"]["capacity_gb"] for item in items)
max_bandwidth = max(
row["bandwidth_gbps"] for row in latest_catalog
if row.get("bandwidth_gbps") is not None
)
best_under_5k = max(
row["memory_gb"] for row in latest_catalog
if row["price_band"] in {"under_3000", "3000_5000"}
and row.get("memory_gb") is not None
)
return {
"generated_at": "2026-06-26",
"bands": [{"key": key, "label": BAND_LABELS[key]} for key in BAND_ORDER],
"producers": PRODUCER_ORDER,
"items": items,
"stats": {
"item_count": len(items),
"catalog_row_count": len(csv_payload["catalog"]),
"max_memory_gb": max_memory,
"max_bandwidth_gbps": max_bandwidth,
"best_under_5k_memory_gb": best_under_5k,
},
**csv_payload,
}
def json_script(payload: dict[str, Any]) -> str:
return (
"window.LOCAL_FRONTIER_DATA = "
+ json.dumps(payload, ensure_ascii=False, separators=(",", ":"))
+ ";\n"
)
def write(path: Path, content: str) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(content, encoding="utf-8")
def page_shell(
*,
title: str,
description: str,
body: str,
asset_prefix: str = "",
page: str = "index",
device_id: str | None = None,
) -> str:
escaped_title = html.escape(title)
escaped_description = html.escape(description)
device_script = (
f"\n<script>window.LOCAL_FRONTIER_DEVICE_ID = {json.dumps(device_id)};</script>"
if device_id else ""
)
return f"""<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{escaped_title}</title>
<meta name="description" content="{escaped_description}">
<link rel="stylesheet" href="{asset_prefix}assets/app.css">
</head>
<body data-page="{page}">
{body}
<script src="{asset_prefix}assets/local-frontier-data.js"></script>{device_script}
<script src="{asset_prefix}assets/app.js"></script>
</body>
</html>
"""
def index_body() -> str:
return """<div class="site-shell">
<header class="masthead">
<nav class="top-nav" aria-label="Primary">
<a class="wordmark" href="index.html">Local Frontier</a>
<div class="nav-links">
<a href="#frontier">Frontier</a>
<a href="#database">Database</a>
<a href="#method">Method</a>
</div>
</nav>
<div class="masthead-grid">
<div>
<p class="eyebrow">Single-machine AI hardware database</p>
<h1>Local Frontier Database</h1>
</div>
<div class="lede">
<p>This is the interactive version of the Local Hardware Frontier writeup. It tracks Apple, NVIDIA, and AMD systems from 2020-2026 by complete-machine price band, memory capacity, bandwidth, and complete-system USD per GB.</p>
<p>Every chart point links back to a source-backed item page. Apple unified memory is included as first-class accelerator-accessible memory, not treated as an afterthought.</p>
</div>
</div>
<div class="stat-strip" id="statStrip" aria-label="Dataset summary"></div>
</header>
<main>
<section class="frontier-panel" id="frontier" aria-labelledby="frontier-title">
<div class="panel-heading">
<div>
<p class="eyebrow">Interactive chart</p>
<h2 id="frontier-title">Frontier Explorer</h2>
</div>
<p>Switch metric and price band. Hover for source rows; click a point to open its item page when the point comes from a specific device.</p>
</div>
<div class="control-grid">
<label>Metric
<select id="metricSelect">
<option value="annual_memory">Annual memory by producer</option>
<option value="annual_price_per_gb">Annual complete-system USD/GB</option>
<option value="annual_bandwidth">Annual memory bandwidth</option>
<option value="best_memory">Best-so-far memory</option>
<option value="best_price_per_gb">Best-so-far complete-system USD/GB</option>
<option value="best_bandwidth">Best-so-far memory bandwidth</option>
<option value="projection">Consumer memory projection to 2030</option>
</select>
</label>
<label>Price band
<select id="bandSelect"></select>
</label>
<div class="producer-toggle" id="producerToggle" aria-label="Producer toggles"></div>
</div>
<div id="frontierChart" class="chart-host" aria-live="polite"></div>
</section>
<section class="section-grid intro-grid" aria-labelledby="what-this-tracks">
<div>
<p class="eyebrow">Scope</p>
<h2 id="what-this-tracks">What This Tracks</h2>
</div>
<div class="prose">
<p>The data tracks accelerator-accessible memory in one physical machine, grouped by complete-machine price band. It covers Apple, NVIDIA, and AMD from 2020 through 2026.</p>
<p>The core question is practical: what can someone buy or configure locally, and how do affordability, capacity, bandwidth, and vendor tradeoffs move over time?</p>
</div>
</section>
<section class="symbol-band" aria-labelledby="symbols">
<div>
<p class="eyebrow">Notation</p>
<h2 id="symbols">How To Read The Symbols</h2>
</div>
<dl class="symbol-list">
<div><dt>U</dt><dd>Unified or coherent memory available to the accelerator.</dd></div>
<div><dt>V</dt><dd>VRAM on one discrete GPU.</dd></div>
<div><dt>&Sigma;</dt><dd>Aggregate installed VRAM across multiple GPUs in one chassis.</dd></div>
<div><dt>*</dt><dd>Price band inferred from a documented component bill of materials.</dd></div>
</dl>
<p class="note">Aggregate VRAM is useful, but it is not the same thing as one unified memory pool. Treat aggregate points as chassis capacity, not a guarantee that one process can use it as one contiguous model memory space.</p>
</section>
<section class="section-grid" aria-labelledby="main-read">
<div>
<p class="eyebrow">Readout</p>
<h2 id="main-read">Main Read</h2>
</div>
<div class="prose multi-column">
<p>Under $3,000, AMD changes the shape of the local market in 2025 and 2026 with 128GB unified memory through Framework Desktop-class hardware. Apple is strong on usable unified memory, but the current sub-$3,000 Apple ceiling in this dataset is 64GB. NVIDIA stays at 24GB in this band because the data tracks complete machines, not used cards or component-only builds.</p>
<p>In the $3,000-$5,000 band, both AMD and NVIDIA reach 128GB unified or coherent memory by 2025-2026. This is the band that matters most for consumer and prosumer local model work because it is expensive but still within a serious personal hardware budget.</p>
<p>In the $5,000-$10,000 band, Apple has the most dramatic historical point: the 512GB M3 Ultra Mac Studio launch configuration in 2025. The best-so-far memory charts keep that 512GB point through 2026 because the historical frontier should not go down just because the current Apple configuration changed.</p>
<p>Above $75,000, NVIDIA dominates the memory and bandwidth frontier. DGX Station-class systems are local in the physical sense, but not local in the affordability sense.</p>
</div>
</section>
<section class="database-panel" id="database" aria-labelledby="database-title">
<div class="panel-heading">
<div>
<p class="eyebrow">Device pages</p>
<h2 id="database-title">Local Frontier Database</h2>
</div>
<p>A TechPowerUp-style catalog for complete local systems and accelerator configurations, including Apple unified-memory machines.</p>
</div>
<div class="table-controls">
<label>Search
<input id="deviceSearch" type="search" placeholder="Mac Studio, DGX, W7900, 128GB">
</label>
<label>Producer
<select id="producerFilter">
<option value="all">All producers</option>
</select>
</label>
<label>Price band
<select id="tableBandFilter">
<option value="all">All bands</option>
</select>
</label>
</div>
<div id="deviceTable" class="table-wrap"></div>
</section>
<section class="section-grid" id="method" aria-labelledby="method-title">
<div>
<p class="eyebrow">Reproducibility</p>
<h2 id="method-title">Method</h2>
</div>
<div class="prose">
<p>The editable source of truth is <code>data/items/*.json</code>. The generator expands each item price history into CSVs, derives annual and best-so-far frontiers, and renders the static Space from those generated artifacts.</p>
<p>The complete-system price-per-memory metric is total configured machine price divided by accelerator-accessible memory. The extracted component memory-price file is separate and should not be confused with complete-machine affordability.</p>
<p>The projection view fits simple least-squares linear trends to 2020-2026 best-so-far memory series. It is a scenario view, not verified future product data.</p>
</div>
</section>
</main>
</div>"""
def device_body(item: dict[str, Any]) -> str:
escaped_name = html.escape(item["name"])
return f"""<div class="site-shell device-shell">
<header class="device-header">
<nav class="top-nav" aria-label="Primary">
<a class="wordmark" href="../../index.html">Local Frontier</a>
<div class="nav-links">
<a href="../../index.html#frontier">Frontier</a>
<a href="../../index.html#database">Database</a>
</div>
</nav>
<a class="back-link" href="../../index.html#database">&larr; Back to database</a>
<p class="eyebrow">{html.escape(item["producer"])}</p>
<h1>{escaped_name}</h1>
<div id="deviceHero" class="device-hero"></div>
</header>
<main id="devicePage" class="device-main"></main>
</div>"""
CSS = r""":root {
color-scheme: dark;
--bg: oklch(16% 0.012 244);
--panel: oklch(21% 0.014 244);
--panel-2: oklch(25% 0.016 244);
--text: oklch(92% 0.015 82);
--muted: oklch(70% 0.018 82);
--faint: oklch(53% 0.018 244);
--line: oklch(34% 0.018 244);
--line-strong: oklch(45% 0.025 244);
--apple: oklch(78% 0.11 195);
--nvidia: oklch(76% 0.16 145);
--amd: oklch(72% 0.18 29);
--accent: oklch(82% 0.14 82);
--blue: oklch(70% 0.14 245);
--green: oklch(74% 0.15 153);
--red: oklch(68% 0.18 27);
--shadow: 0 20px 70px rgb(0 0 0 / 0.24);
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 24px;
--space-6: 32px;
--space-7: 48px;
--space-8: 64px;
font-family: "Aptos", "Segoe UI", "Helvetica Neue", ui-sans-serif, system-ui, sans-serif;
}
* {
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
margin: 0;
background: var(--bg);
color: var(--text);
}
a {
color: inherit;
text-decoration-color: color-mix(in oklch, currentColor 45%, transparent);
text-underline-offset: 0.18em;
}
button,
input,
select {
font: inherit;
}
code,
table,
.eyebrow,
.stat-value,
.device-table {
font-family: "Aptos Mono", "SFMono-Regular", Consolas, ui-monospace, monospace;
}
.site-shell {
width: min(1480px, calc(100vw - 32px));
margin: 0 auto;
}
.masthead {
padding: var(--space-5) 0 var(--space-5);
}
.device-header {
padding: var(--space-5) 0 var(--space-7);
}
.top-nav {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-5);
padding: var(--space-3) 0 var(--space-6);
border-bottom: 1px solid var(--line);
}
.wordmark {
font-weight: 800;
letter-spacing: 0.02em;
text-transform: uppercase;
text-decoration: none;
}
.nav-links {
display: flex;
gap: var(--space-5);
color: var(--muted);
font-size: 0.94rem;
}
.masthead-grid {
display: grid;
grid-template-columns: minmax(300px, 0.75fr) minmax(320px, 0.9fr);
gap: clamp(var(--space-5), 4vw, var(--space-7));
align-items: end;
padding-top: var(--space-5);
}
h1,
h2,
h3,
p {
margin: 0;
}
h1 {
max-width: 18ch;
font-size: clamp(2.35rem, 5.2vw, 5rem);
line-height: 0.95;
letter-spacing: 0;
}
h2 {
font-size: clamp(1.5rem, 2.4vw, 2.35rem);
line-height: 1.02;
letter-spacing: 0;
}
h3 {
font-size: 1.05rem;
line-height: 1.2;
}
.eyebrow {
margin-bottom: var(--space-3);
color: var(--accent);
font-size: 0.76rem;
font-weight: 800;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.lede {
display: grid;
gap: var(--space-4);
color: var(--muted);
font-size: 1.04rem;
line-height: 1.65;
}
.stat-strip {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 1px;
margin-top: var(--space-5);
background: var(--line);
border: 1px solid var(--line);
}
.stat-cell {
min-width: 0;
min-height: 88px;
padding: var(--space-4) var(--space-5);
background: var(--panel);
}
.stat-value {
display: block;
color: var(--text);
overflow-wrap: anywhere;
font-size: clamp(1.3rem, 2.4vw, 2.15rem);
line-height: 1;
}
.stat-label {
display: block;
margin-top: var(--space-3);
color: var(--muted);
font-size: 0.86rem;
}
main {
display: grid;
gap: var(--space-7);
padding-bottom: var(--space-8);
}
.section-grid,
.symbol-band,
.frontier-panel,
.database-panel {
border-top: 1px solid var(--line);
padding-top: var(--space-6);
}
.section-grid {
display: grid;
grid-template-columns: 280px minmax(0, 1fr);
gap: var(--space-7);
}
.prose {
display: grid;
gap: var(--space-4);
max-width: 82ch;
color: var(--muted);
line-height: 1.66;
}
.multi-column {
column-width: 34ch;
display: block;
}
.multi-column p {
break-inside: avoid;
margin-bottom: var(--space-4);
}
.symbol-band {
display: grid;
grid-template-columns: 280px 1fr;
gap: var(--space-7);
}
.symbol-list {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 1px;
margin: 0;
background: var(--line);
border: 1px solid var(--line);
}
.symbol-list div {
padding: var(--space-5);
background: var(--panel);
}
.symbol-list dt {
margin-bottom: var(--space-3);
font-size: 2rem;
font-weight: 900;
}
.symbol-list dd {
margin: 0;
color: var(--muted);
line-height: 1.45;
}
.note {
grid-column: 2;
max-width: 82ch;
color: var(--faint);
line-height: 1.6;
}
.frontier-panel,
.database-panel {
display: grid;
gap: var(--space-5);
min-width: 0;
}
.panel-heading {
display: grid;
grid-template-columns: minmax(260px, 0.4fr) minmax(0, 0.65fr);
gap: var(--space-6);
align-items: end;
}
.panel-heading p {
color: var(--muted);
line-height: 1.55;
}
.control-grid,
.table-controls {
display: grid;
grid-template-columns: repeat(4, minmax(180px, 1fr));
gap: var(--space-3);
align-items: end;
}
label {
display: grid;
gap: var(--space-2);
color: var(--muted);
font-size: 0.8rem;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
}
select,
input {
width: 100%;
min-height: 42px;
border: 1px solid var(--line-strong);
border-radius: 6px;
background: var(--panel);
color: var(--text);
padding: 0 var(--space-3);
letter-spacing: 0;
text-transform: none;
}
select:focus,
input:focus,
button:focus-visible,
a:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 3px;
}
.producer-toggle {
display: flex;
flex-wrap: wrap;
gap: var(--space-2);
}
.chip {
min-height: 42px;
border: 1px solid var(--line-strong);
border-radius: 999px;
background: var(--panel);
color: var(--muted);
padding: 0 var(--space-4);
cursor: pointer;
}
.chip[aria-pressed="true"] {
color: var(--text);
border-color: color-mix(in oklch, var(--accent) 55%, var(--line-strong));
background: color-mix(in oklch, var(--accent) 12%, var(--panel));
}
.chart-host {
position: relative;
min-height: 520px;
border: 1px solid var(--line);
background: var(--panel);
box-shadow: var(--shadow);
}
.chart-host svg {
display: block;
width: 100%;
height: auto;
}
.chart-title {
padding: var(--space-5) var(--space-5) 0;
}
.chart-subtitle {
margin-top: var(--space-2);
color: var(--muted);
font-size: 0.92rem;
}
.tooltip {
position: absolute;
min-width: 220px;
max-width: 360px;
pointer-events: none;
border: 1px solid var(--line-strong);
background: oklch(18% 0.014 244 / 0.96);
color: var(--text);
padding: var(--space-3);
box-shadow: var(--shadow);
transform: translate(-50%, calc(-100% - 18px));
line-height: 1.35;
z-index: 5;
}
.tooltip strong {
display: block;
margin-bottom: var(--space-2);
}
.tooltip span {
display: block;
color: var(--muted);
}
.table-wrap {
overflow-x: auto;
border: 1px solid var(--line);
background: var(--panel);
min-width: 0;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 0.9rem;
}
th,
td {
border-bottom: 1px solid var(--line);
padding: 12px 14px;
text-align: left;
vertical-align: top;
}
th {
position: sticky;
top: 0;
z-index: 1;
background: var(--panel-2);
color: var(--muted);
font-size: 0.72rem;
letter-spacing: 0.08em;
text-transform: uppercase;
}
td {
color: var(--muted);
}
td strong,
.device-link {
color: var(--text);
}
.pill {
display: inline-flex;
align-items: center;
min-height: 24px;
border: 1px solid var(--line-strong);
border-radius: 999px;
padding: 2px 8px;
color: var(--muted);
font-size: 0.76rem;
white-space: nowrap;
}
.producer-Apple {
color: var(--apple);
}
.producer-NVIDIA {
color: var(--nvidia);
}
.producer-AMD {
color: var(--amd);
}
.back-link {
display: inline-flex;
margin-top: var(--space-6);
margin-bottom: var(--space-5);
color: var(--muted);
font-size: 0.94rem;
}
.device-header h1 {
max-width: 22ch;
font-size: clamp(2.3rem, 6vw, 5.4rem);
line-height: 0.96;
}
.device-hero {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 1px;
margin-top: var(--space-6);
background: var(--line);
border: 1px solid var(--line);
}
.device-hero .stat-value {
font-size: clamp(1.15rem, 2.3vw, 2rem);
}
.device-main {
display: grid;
grid-template-columns: minmax(0, 0.9fr) minmax(320px, 0.45fr);
gap: var(--space-7);
align-items: start;
min-width: 0;
}
.detail-block {
display: grid;
gap: var(--space-4);
border-top: 1px solid var(--line);
padding-top: var(--space-5);
min-width: 0;
}
.detail-card {
border: 1px solid var(--line);
background: var(--panel);
padding: var(--space-5);
min-width: 0;
}
.detail-list {
display: grid;
gap: var(--space-3);
margin: 0;
}
.detail-list div {
display: grid;
grid-template-columns: 160px 1fr;
gap: var(--space-4);
}
.detail-list dt {
color: var(--faint);
}
.detail-list dd {
margin: 0;
color: var(--muted);
}
.source-list {
display: grid;
gap: var(--space-3);
margin: 0;
padding: 0;
list-style: none;
}
.source-list li {
color: var(--muted);
line-height: 1.45;
}
.empty {
padding: var(--space-6);
color: var(--muted);
}
@media (max-width: 980px) {
.masthead-grid,
.section-grid,
.symbol-band,
.panel-heading,
.device-main {
grid-template-columns: 1fr;
}
.note {
grid-column: auto;
}
.stat-strip,
.symbol-list,
.device-hero,
.control-grid,
.table-controls {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 640px) {
.site-shell {
width: min(100% - 20px, 1480px);
}
.top-nav,
.nav-links {
align-items: flex-start;
flex-direction: column;
}
h1 {
font-size: clamp(2.15rem, 11vw, 3.2rem);
}
.device-header h1 {
font-size: clamp(2rem, 10vw, 3rem);
}
.stat-strip,
.symbol-list,
.device-hero,
.control-grid,
.table-controls {
grid-template-columns: 1fr;
}
.chart-host {
min-height: 430px;
}
.detail-list div {
grid-template-columns: 1fr;
gap: var(--space-1);
}
table {
font-size: 0.82rem;
}
}
"""
JS = r"""const DATA = window.LOCAL_FRONTIER_DATA;
const byId = new Map(DATA.items.map((item) => [item.id, item]));
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) => ({
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': "&quot;",
"'": "&#39;"
})[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 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 fmtUsdPerGB(value) {
return value === null || value === undefined || value === "" ? "-" : `$${fmtNumber(value, 2)}/GB`;
}
function deviceHref(id, prefix = "") {
return `${prefix}devices/${id}/index.html`;
}
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 renderStats() {
const host = $("#statStrip");
if (!host) return;
const stats = [
[DATA.stats.item_count, "source item pages"],
[DATA.stats.catalog_row_count, "priced catalog rows"],
[`${DATA.stats.max_memory_gb}GB`, "maximum accelerator memory"],
[`${DATA.stats.best_under_5k_memory_gb}GB`, "best current memory under $5k"]
];
host.innerHTML = stats.map(([value, label]) => `
<div class="stat-cell">
<span class="stat-value">${escapeHTML(value)}</span>
<span class="stat-label">${escapeHTML(label)}</span>
</div>
`).join("");
}
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) {
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 active = activeProducers();
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 producer", "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 producer 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.producer,
item.memory.type,
item.accelerators.map((accel) => accel.name).join(" ")
].join(" ").toLowerCase();
return (!search || haystack.includes(search))
&& (producer === "all" || 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>Producer</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><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><span class="producer-${item.producer}">${item.producer}</span></td>
<td>${fmtGB(item.memory.capacity_gb)} <span class="pill">${escapeHTML(item.memory.symbol)}</span><br>${escapeHTML(item.memory.type)}</td>
<td>${fmtBandwidth(item.memory.bandwidth_gbps)}</td>
<td>${fmtMoney(event.price_usd)}<br><span class="pill">${escapeHTML(event.price_kind.replaceAll("_", " "))}</span></td>
<td>${escapeHTML(DATA.bands.find((entry) => entry.key === event.price_band)?.label || event.price_band)}</td>
<td>${escapeHTML(event.evidence)}<br><span class="pill">${escapeHTML(event.confidence)}</span></td>
</tr>`;
}).join("")}
</tbody>
</table>
`;
}
function renderDevicePage() {
const id = window.LOCAL_FRONTIER_DEVICE_ID;
const item = byId.get(id);
if (!item) return;
const event = currentPriceEvent(item);
const hero = $("#deviceHero");
if (hero) {
hero.innerHTML = [
[`${item.memory.capacity_gb}GB`, `${item.memory.symbol} ${item.memory.type}`],
[fmtBandwidth(item.memory.bandwidth_gbps), "source-backed peak memory bandwidth"],
[fmtMoney(event.price_usd), `${event.end_year} ${event.price_kind.replaceAll("_", " ")}`],
[DATA.bands.find((entry) => entry.key === event.price_band)?.label || event.price_band, "complete-system price band"]
].map(([value, label]) => `
<div class="stat-cell">
<span class="stat-value">${escapeHTML(value)}</span>
<span class="stat-label">${escapeHTML(label)}</span>
</div>
`).join("");
}
const accelerators = item.accelerators.length
? item.accelerators.map((accel) => `${accel.count ? `${accel.count}x ` : ""}${accel.vendor} ${accel.name}`).join("<br>")
: "Not specified separately";
const frontierRows = [
...DATA.annual.filter((row) => row.item_id === item.id),
...DATA.dominant.filter((row) => row.item_id === item.id)
];
const host = $("#devicePage");
host.innerHTML = `
<section class="detail-block">
<div class="detail-card">
<h2>Specification</h2>
<dl class="detail-list">
<div><dt>Producer</dt><dd><span class="producer-${item.producer}">${item.producer}</span></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>${accelerators}</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>Frontier Appearances</h2>
${frontierRows.length ? frontierRowsTable(frontierRows) : `<p class="empty">This item is in the catalog but does not become a selected annual or best-so-far frontier row.</p>`}
</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>
`;
}
function priceHistoryTable(item) {
return `<table><thead><tr><th>Years</th><th>Price</th><th>Band</th><th>Evidence</th></tr></thead><tbody>
${item.price_history.map((event) => `<tr>
<td>${event.start_year}-${event.end_year}</td>
<td>${fmtMoney(event.price_usd)}</td>
<td>${escapeHTML(DATA.bands.find((entry) => entry.key === event.price_band)?.label || event.price_band)}</td>
<td>${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>Band</th><th>Price/GB</th></tr></thead><tbody>
${rows.map((row) => `<tr>
<td>${row.start_year}-${row.end_year}</td>
<td>${fmtMoney(row.price_usd)}</td>
<td>${escapeHTML(DATA.bands.find((entry) => entry.key === row.price_band)?.label || row.price_band)}</td>
<td>${fmtUsdPerGB(row.price_per_memory_gb)}</td>
</tr>`).join("")}
</tbody></table>`;
}
function frontierRowsTable(rows) {
return `<div class="table-wrap"><table><thead><tr><th>Year</th><th>Band</th><th>Metric</th><th>Value</th></tr></thead><tbody>
${rows.map((row) => `<tr>
<td>${row.year}</td>
<td>${escapeHTML(DATA.bands.find((entry) => entry.key === row.price_band)?.label || row.price_band)}</td>
<td>${row.metric_name ? escapeHTML(row.metric_name.replaceAll("_", " ")) : "memory frontier"}</td>
<td>${row.metric_value !== undefined ? escapeHTML(String(row.metric_value)) : fmtGB(row.memory_gb)}</td>
</tr>`).join("")}
</tbody></table></div>`;
}
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>`;
}
function init() {
if (document.body.dataset.page === "device") {
renderDevicePage();
return;
}
renderStats();
setupControls();
renderFrontierChart();
renderDeviceTable();
}
init();
"""
def space_readme() -> str:
return """---
title: Local Frontier
sdk: static
app_file: index.html
license: mit
short_description: Interactive local AI hardware frontier database.
---
# Local Frontier
Interactive static database for the local hardware frontier notes. Generated from
the item JSON and CSV artifacts in `docs/local_hardware_frontier/`.
"""
def build() -> None:
payload = build_payload()
if SPACE_DIR.exists():
shutil.rmtree(SPACE_DIR)
ASSETS_DIR.mkdir(parents=True)
DEVICES_DIR.mkdir(parents=True)
write(SPACE_DIR / "README.md", space_readme())
write(ASSETS_DIR / "app.css", CSS)
write(ASSETS_DIR / "app.js", JS)
write(ASSETS_DIR / "local-frontier-data.js", json_script(payload))
write(
SPACE_DIR / "index.html",
page_shell(
title="Local Frontier",
description="Interactive local AI hardware frontier database.",
body=index_body(),
),
)
for item in payload["items"]:
write(
DEVICES_DIR / item["id"] / "index.html",
page_shell(
title=f"{item['name']} - Local Frontier",
description=f"{item['producer']} local accelerator memory item page.",
body=device_body(item),
asset_prefix="../../",
page="device",
device_id=item["id"],
),
)
print(f"Built static Space at {SPACE_DIR}")
print(f"Device pages: {len(payload['items'])}")
if __name__ == "__main__":
build()

Xet Storage Details

Size:
47.9 kB
·
Xet hash:
6e8dff268250fe2ef12730d77a69456e6840fbcb412214162fac063a855fb400

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.