Download static/index.html from dejanai/fanout-diffusion: direct link, hf CLI and curl.
- Browser
- Download file 75.2 kB
-
https://huggingface.co/dejanai/fanout-diffusion/resolve/main/static/index.html
- Command line
-
hf download hf://dejanai/fanout-diffusion/static/index.html
-
curl -L -o index.html https://huggingface.co/dejanai/fanout-diffusion/resolve/main/static/index.html
75.2 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>R4T Diffusion Retriever • Experiment Journal</title> | |
| <!-- Plotly.js --> | |
| <script src="https://cdn.plot.ly/plotly-2.35.2.min.js"></script> | |
| <!-- ECharts 5 (Parallel Coordinates Rank Chart) --> | |
| <script src="https://cdn.jsdelivr.net/npm/echarts@5.5.1/dist/echarts.min.js"></script> | |
| <style> | |
| :root { | |
| --bg-dark: #0d1117; | |
| --card-bg: #161b22; | |
| --card-border: #30363d; | |
| --text-main: #c9d1d9; | |
| --text-muted: #8b949e; | |
| --accent-blue: #58a6ff; | |
| --accent-green: #3fb950; | |
| --accent-purple: #bc8cff; | |
| --accent-orange: #d29922; | |
| --accent-red: #f85149; | |
| } | |
| * { box-sizing: border-box; margin: 0; padding: 0; } | |
| body { | |
| background-color: var(--bg-dark); | |
| color: var(--text-main); | |
| font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; | |
| line-height: 1.5; | |
| padding: 0; | |
| } | |
| header { | |
| background: #090d13; | |
| border-bottom: 1px solid var(--card-border); | |
| padding: 1rem 2rem; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| position: sticky; | |
| top: 0; | |
| z-index: 100; | |
| } | |
| .brand { | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| } | |
| .logo-badge { | |
| background: linear-gradient(135deg, #1f6feb, #8957e5); | |
| color: white; | |
| font-weight: 800; | |
| padding: 4px 10px; | |
| border-radius: 6px; | |
| font-size: 0.9rem; | |
| letter-spacing: 0.5px; | |
| } | |
| .title { | |
| font-size: 1.25rem; | |
| font-weight: 700; | |
| color: #f0f6fc; | |
| } | |
| .nav-tabs { | |
| display: flex; | |
| gap: 8px; | |
| } | |
| .tab-btn { | |
| background: transparent; | |
| border: 1px solid transparent; | |
| color: var(--text-muted); | |
| padding: 8px 16px; | |
| border-radius: 6px; | |
| cursor: pointer; | |
| font-size: 0.9rem; | |
| font-weight: 600; | |
| transition: all 0.2s ease; | |
| } | |
| .tab-btn:hover { | |
| color: var(--text-main); | |
| background: rgba(255,255,255,0.05); | |
| } | |
| .tab-btn.active { | |
| color: #f0f6fc; | |
| background: var(--card-bg); | |
| border-color: var(--card-border); | |
| } | |
| .container { | |
| width: 100%; | |
| max-width: 100%; | |
| margin: 0; | |
| padding: 1.25rem 2rem; | |
| } | |
| .tab-content { | |
| display: none; | |
| } | |
| .tab-content.active { | |
| display: block; | |
| } | |
| /* KPI Grid */ | |
| .kpi-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); | |
| gap: 1rem; | |
| margin-bottom: 1.5rem; | |
| } | |
| .kpi-card { | |
| background: var(--card-bg); | |
| border: 1px solid var(--card-border); | |
| border-radius: 8px; | |
| padding: 1.25rem; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .kpi-card::before { | |
| content: ""; | |
| position: absolute; | |
| top: 0; left: 0; right: 0; height: 3px; | |
| background: var(--kpi-color, var(--accent-blue)); | |
| } | |
| .kpi-title { | |
| font-size: 0.8rem; | |
| text-transform: uppercase; | |
| letter-spacing: 0.5px; | |
| color: var(--text-muted); | |
| margin-bottom: 6px; | |
| } | |
| .kpi-value { | |
| font-size: 1.85rem; | |
| font-weight: 700; | |
| color: #f0f6fc; | |
| margin-bottom: 4px; | |
| } | |
| .kpi-sub { | |
| font-size: 0.82rem; | |
| color: var(--text-muted); | |
| } | |
| /* Charts Grid */ | |
| .chart-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(460px, 1fr)); | |
| gap: 1.25rem; | |
| margin-bottom: 1.5rem; | |
| width: 100%; | |
| } | |
| .chart-card { | |
| background: var(--card-bg); | |
| border: 1px solid var(--card-border); | |
| border-radius: 8px; | |
| padding: 1rem; | |
| overflow: hidden ; | |
| min-width: 0 ; | |
| position: relative; | |
| } | |
| .chart-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 0.75rem; | |
| padding-bottom: 0.5rem; | |
| border-bottom: 1px solid rgba(255,255,255,0.06); | |
| } | |
| .chart-title { | |
| font-size: 0.95rem; | |
| font-weight: 600; | |
| color: #f0f6fc; | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| .chart-box { | |
| width: 100% ; | |
| height: 380px; | |
| } | |
| /* Matrix / Scorecard */ | |
| .matrix-card { | |
| background: var(--card-bg); | |
| border: 1px solid var(--card-border); | |
| border-radius: 8px; | |
| padding: 1.25rem; | |
| margin-bottom: 1.5rem; | |
| } | |
| .matrix-title { | |
| font-size: 1.05rem; | |
| font-weight: 700; | |
| color: #f0f6fc; | |
| margin-bottom: 1rem; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .matrix-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); | |
| gap: 1rem; | |
| } | |
| .pillar-card { | |
| background: rgba(255,255,255,0.02); | |
| border: 1px solid rgba(255,255,255,0.08); | |
| border-left: 3px solid var(--pillar-color, var(--accent-blue)); | |
| border-radius: 6px; | |
| padding: 1rem; | |
| } | |
| .pillar-phase { | |
| font-size: 0.75rem; | |
| text-transform: uppercase; | |
| font-weight: 700; | |
| color: var(--pillar-color, var(--accent-blue)); | |
| margin-bottom: 4px; | |
| } | |
| .pillar-name { | |
| font-size: 1rem; | |
| font-weight: 600; | |
| color: #f0f6fc; | |
| margin-bottom: 8px; | |
| } | |
| .pillar-stats { | |
| font-size: 0.85rem; | |
| color: var(--text-muted); | |
| display: flex; | |
| flex-direction: column; | |
| gap: 4px; | |
| } | |
| .pillar-stats b { | |
| color: #f0f6fc; | |
| } | |
| /* Tables */ | |
| .table-card { | |
| background: var(--card-bg); | |
| border: 1px solid var(--card-border); | |
| border-radius: 8px; | |
| overflow: hidden; | |
| margin-bottom: 1.5rem; | |
| } | |
| .table-toolbar { | |
| padding: 1rem; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| gap: 1rem; | |
| flex-wrap: wrap; | |
| border-bottom: 1px solid var(--card-border); | |
| } | |
| .search-input { | |
| background: #0d1117; | |
| border: 1px solid var(--card-border); | |
| color: var(--text-main); | |
| padding: 6px 12px; | |
| border-radius: 6px; | |
| font-size: 0.88rem; | |
| width: 320px; | |
| outline: none; | |
| } | |
| .search-input:focus { | |
| border-color: var(--accent-blue); | |
| } | |
| .filter-pills { | |
| display: flex; | |
| gap: 6px; | |
| flex-wrap: wrap; | |
| } | |
| .pill { | |
| background: rgba(255,255,255,0.05); | |
| border: 1px solid rgba(255,255,255,0.1); | |
| color: var(--text-muted); | |
| padding: 4px 10px; | |
| border-radius: 20px; | |
| font-size: 0.8rem; | |
| cursor: pointer; | |
| } | |
| .pill.active { | |
| background: var(--accent-blue); | |
| color: #0d1117; | |
| font-weight: 600; | |
| border-color: var(--accent-blue); | |
| } | |
| table { | |
| width: 100%; | |
| border-collapse: collapse; | |
| font-size: 0.88rem; | |
| } | |
| th, td { | |
| padding: 10px 14px; | |
| text-align: left; | |
| border-bottom: 1px solid rgba(255,255,255,0.05); | |
| } | |
| th { | |
| background: #12161c; | |
| color: var(--text-muted); | |
| font-weight: 600; | |
| user-select: none; | |
| } | |
| th.sortable { cursor: pointer; } | |
| th.sortable:hover { color: #f0f6fc; } | |
| tr:hover td { background: rgba(255,255,255,0.02); } | |
| .badge { | |
| display: inline-block; | |
| padding: 2px 8px; | |
| border-radius: 12px; | |
| font-size: 0.75rem; | |
| font-weight: 600; | |
| } | |
| .badge-success { background: rgba(63, 185, 80, 0.15); color: #3fb950; border: 1px solid rgba(63, 185, 80, 0.3); } | |
| .badge-running { background: rgba(210, 153, 34, 0.15); color: #d29922; border: 1px solid rgba(210, 153, 34, 0.3); } | |
| .badge-failed { background: rgba(248, 81, 73, 0.15); color: #f85149; border: 1px solid rgba(248, 81, 73, 0.3); } | |
| .badge-info { background: rgba(88, 166, 255, 0.15); color: #58a6ff; border: 1px solid rgba(88, 166, 255, 0.3); } | |
| .btn-action { | |
| background: transparent; | |
| border: 1px solid var(--card-border); | |
| color: var(--accent-blue); | |
| padding: 4px 8px; | |
| border-radius: 4px; | |
| cursor: pointer; | |
| font-size: 0.8rem; | |
| } | |
| .btn-action:hover { | |
| background: rgba(88, 166, 255, 0.1); | |
| border-color: var(--accent-blue); | |
| } | |
| /* W&B Drill Down Header */ | |
| .drill-header { | |
| background: var(--card-bg); | |
| border: 1px solid var(--card-border); | |
| border-radius: 8px; | |
| padding: 1.25rem; | |
| margin-bottom: 1.5rem; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| gap: 1rem; | |
| flex-wrap: wrap; | |
| } | |
| /* WandB Style Sample Inspector */ | |
| .sample-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(420px, 1fr)); | |
| gap: 1rem; | |
| margin-bottom: 1.5rem; | |
| } | |
| .sample-card { | |
| background: #12171f; | |
| border: 1px solid var(--card-border); | |
| border-radius: 6px; | |
| padding: 1rem; | |
| } | |
| .sample-head { | |
| display: flex; | |
| justify-content: space-between; | |
| margin-bottom: 8px; | |
| font-size: 0.8rem; | |
| color: var(--text-muted); | |
| } | |
| .subquery-list { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 4px; | |
| margin-top: 8px; | |
| } | |
| .subquery-item { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| background: rgba(255,255,255,0.03); | |
| padding: 6px 10px; | |
| border-radius: 4px; | |
| font-size: 0.85rem; | |
| border-left: 2px solid var(--accent-blue); | |
| } | |
| .subquery-slot { | |
| color: var(--text-muted); | |
| font-size: 0.75rem; | |
| font-weight: 700; | |
| margin-right: 8px; | |
| } | |
| .json-view { | |
| background: #090d13; | |
| padding: 12px; | |
| border-radius: 6px; | |
| font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; | |
| font-size: 0.82rem; | |
| color: #79c0ff; | |
| overflow-x: auto; | |
| max-height: 280px; | |
| } | |
| /* W&B Parallel Coordinates Rank Chart */ | |
| #parcoords-wrap { | |
| position: relative; | |
| width: 100%; | |
| height: 480px; | |
| background: #11161d; | |
| border: 1px solid var(--card-border); | |
| border-radius: 8px; | |
| margin-top: 14px; | |
| } | |
| #parcoords-echart { | |
| width: 100%; | |
| height: 100%; | |
| } | |
| #parcoords-handles { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 45px; | |
| pointer-events: none; | |
| } | |
| .pc-handle { | |
| pointer-events: auto; | |
| position: absolute; | |
| transform: translateX(-50%); | |
| height: 28px; | |
| line-height: 26px; | |
| padding: 0 10px; | |
| font-size: 11.5px; | |
| font-weight: 700; | |
| text-align: center; | |
| white-space: nowrap; | |
| cursor: grab; | |
| user-select: none; | |
| border-radius: 6px; | |
| background: #21262d; | |
| color: #c9d1d9; | |
| border: 1px solid #30363d; | |
| box-shadow: 0 2px 4px rgba(0,0,0,0.3); | |
| transition: background .12s, border-color .12s, color .12s; | |
| } | |
| .pc-handle:hover { | |
| background: #30363d; | |
| border-color: #58a6ff; | |
| color: #58a6ff; | |
| } | |
| .pc-handle.pc-dragging { | |
| opacity: .35; | |
| cursor: grabbing; | |
| } | |
| .pc-handle.pc-over { | |
| background: rgba(88, 166, 255, 0.25); | |
| border-color: #58a6ff; | |
| color: #fff; | |
| } | |
| #parcoords-hint { | |
| font-size: 11.5px; | |
| color: var(--text-muted); | |
| margin-top: 10px; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| flex-wrap: wrap; | |
| gap: 8px; | |
| } | |
| .rank-badge-gold { | |
| background: rgba(210, 153, 34, 0.25); | |
| color: #e3b341; | |
| border: 1px solid rgba(210, 153, 34, 0.5); | |
| font-weight: 700; | |
| padding: 2px 8px; | |
| border-radius: 4px; | |
| font-size: 0.78rem; | |
| } | |
| .rank-badge-silver { | |
| background: rgba(139, 148, 158, 0.25); | |
| color: #c9d1d9; | |
| border: 1px solid rgba(139, 148, 158, 0.5); | |
| font-weight: 700; | |
| padding: 2px 8px; | |
| border-radius: 4px; | |
| font-size: 0.78rem; | |
| } | |
| .rank-badge-bronze { | |
| background: rgba(248, 81, 73, 0.25); | |
| color: #f85149; | |
| border: 1px solid rgba(248, 81, 73, 0.5); | |
| font-weight: 700; | |
| padding: 2px 8px; | |
| border-radius: 4px; | |
| font-size: 0.78rem; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <div class="brand"> | |
| <div class="logo-badge">R4T DIFFUSION</div> | |
| <div class="title">Continuous Diffusion Retriever • Experiment Journal</div> | |
| </div> | |
| <div class="nav-tabs"> | |
| <button class="tab-btn active" onclick="switchTab('overview')">Overview & Rank Chart</button> | |
| <button class="tab-btn" onclick="switchTab('runs')">Runs Table</button> | |
| <button class="tab-btn" onclick="switchTab('drilldown')">W&B Sample Inspector</button> | |
| <button class="tab-btn" onclick="switchTab('playground')" style="border: 1px solid rgba(63, 185, 80, 0.4); color: #3fb950; font-weight: 700;">⚡ Live Fanout Playground (169µs)</button> | |
| <button class="btn-action" onclick="loadAllData()" style="margin-left: 8px;">↻ Refresh</button> | |
| </div> | |
| </header> | |
| <div class="container"> | |
| <!-- TAB 1: OVERVIEW & PARETO FRONTIER --> | |
| <div id="tab-overview" class="tab-content active"> | |
| <!-- KPI ROW --> | |
| <div class="kpi-grid"> | |
| <div class="kpi-card" style="--kpi-color: var(--accent-blue);"> | |
| <div class="kpi-title">Total Sweeps & Runs</div> | |
| <div id="kpi-total-runs" class="kpi-value">-</div> | |
| <div class="kpi-sub">SQLite WAL Multi-Process Journal</div> | |
| </div> | |
| <div class="kpi-card" style="--kpi-color: var(--accent-green);"> | |
| <div class="kpi-title">Best Validation Loss</div> | |
| <div id="kpi-best-val" class="kpi-value">-</div> | |
| <div class="kpi-sub" id="kpi-best-sub">EDM Weighted MSE</div> | |
| </div> | |
| <div class="kpi-card" style="--kpi-color: var(--accent-purple);"> | |
| <div class="kpi-title">Peak Pairwise Diversity</div> | |
| <div id="kpi-diversity" class="kpi-value">-</div> | |
| <div class="kpi-sub">Cosine Distance across 10 Slots</div> | |
| </div> | |
| <div class="kpi-card" style="--kpi-color: var(--accent-orange);"> | |
| <div class="kpi-title">Sampling Latency (RTX 4090)</div> | |
| <div id="kpi-latency" class="kpi-value">-</div> | |
| <div class="kpi-sub">16-Step Heun ODE / 10 Vectors</div> | |
| </div> | |
| </div> | |
| <!-- WANDB STYLE MULTI-PARAMETER PARALLEL COORDINATES SWEEP RANK CHART --> | |
| <div class="matrix-card" id="sweep-rank-section" style="margin-bottom: 1.5rem; padding: 1.25rem;"> | |
| <div style="display: flex; justify-content: space-between; align-items: flex-start; flex-wrap: wrap; gap: 12px; margin-bottom: 12px;"> | |
| <div> | |
| <div style="display: flex; align-items: center; gap: 10px;"> | |
| <span class="badge badge-purple" style="font-size: 0.75rem; text-transform: uppercase;">W&B Sweep View</span> | |
| <h3 style="color: #f0f6fc; font-size: 1.2rem; font-weight: 700; margin: 0;">Multi-Parameter Parallel Coordinates (Rank Chart)</h3> | |
| </div> | |
| <p style="color: var(--text-muted); font-size: 0.82rem; margin-top: 4px;"> | |
| Drag vertical axis pills left or right to reorder pillars. Drag vertically along any axis to brush-filter runs. Hover over splines to inspect configuration & metrics. Click any line to jump to decoded subqueries. | |
| </p> | |
| </div> | |
| <div style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap;"> | |
| <select id="sweep-rank-metric" class="search-input" style="width: auto; padding: 5px 10px; font-size: 0.82rem;" onchange="renderSweepRankChart()"> | |
| <option value="val_loss">Rank by: Best Val Loss (Min)</option> | |
| <option value="alignment">Rank by: Prompt Alignment (Max)</option> | |
| <option value="diversity">Rank by: Pairwise Diversity (Max)</option> | |
| <option value="latency">Rank by: Sampling Latency (Min)</option> | |
| </select> | |
| <select id="sweep-filter-scope" class="search-input" style="width: auto; padding: 5px 10px; font-size: 0.82rem;" onchange="renderSweepRankChart()"> | |
| <option value="all">Scope: All Database Runs</option> | |
| <option value="540k">Scope: 540k Full Corpus</option> | |
| <option value="binary">Scope: 1-Bit & Binary</option> | |
| <option value="sweeps">Scope: Sweeps Only</option> | |
| <option value="frontier">Scope: 1-Bit Frontier Exploration</option> | |
| </select> | |
| <button class="pill" onclick="resetPillars(event)" style="font-size: 0.8rem; padding: 5px 12px;">Reset Pillars</button> | |
| </div> | |
| </div> | |
| <div id="parcoords-wrap"> | |
| <div id="parcoords-echart"></div> | |
| <div id="parcoords-handles"></div> | |
| </div> | |
| <div id="parcoords-hint"> | |
| <span>💡 Tip: Drag an axis title to reorder pillars. Brush along any axis to filter runs. Active: <b id="pc-selected-count" style="color: #f0f6fc;">8 / 8</b> runs.</span> | |
| <span>Lines colored by <b id="pc-color-label" style="color: var(--accent-green);">Rank (Best → Low)</b></span> | |
| </div> | |
| <!-- SWEEP LEADERBOARD TABLE --> | |
| <div style="margin-top: 1.25rem; border-top: 1px solid var(--card-border); padding-top: 1rem;"> | |
| <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.75rem;"> | |
| <div style="font-size: 0.95rem; font-weight: 700; color: #f0f6fc;">Hyperparameter Sweep Ranked Leaderboard</div> | |
| <div id="leaderboard-count" style="font-size: 0.8rem; color: var(--text-muted);">8 runs ranked</div> | |
| </div> | |
| <div style="overflow-x: auto;"> | |
| <table style="width: 100%; border-collapse: collapse; font-size: 0.84rem;"> | |
| <thead> | |
| <tr style="border-bottom: 1px solid var(--card-border); color: var(--text-muted); text-align: left;"> | |
| <th style="padding: 8px 10px; width: 110px;">Rank</th> | |
| <th style="padding: 8px 10px;">Run Name</th> | |
| <th style="padding: 8px 10px;">Depth</th> | |
| <th style="padding: 8px 10px;">Learning Rate</th> | |
| <th style="padding: 8px 10px;">Slot Ordering</th> | |
| <th style="padding: 8px 10px;">Best Val Loss</th> | |
| <th style="padding: 8px 10px;">Prompt Alignment</th> | |
| <th style="padding: 8px 10px;">Diversity</th> | |
| <th style="padding: 8px 10px;">Latency</th> | |
| <th style="padding: 8px 10px; text-align: center;">Actions</th> | |
| </tr> | |
| </thead> | |
| <tbody id="sweep-leaderboard-body"> | |
| <!-- Populated dynamically --> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- CHARTS ROW 1: CONVERGENCE & PARETO FRONTIER --> | |
| <div class="chart-grid"> | |
| <div class="chart-card"> | |
| <div class="chart-header"> | |
| <div class="chart-title"> | |
| <span>Training Convergence Trajectories (All Runs)</span> | |
| </div> | |
| <span class="badge badge-info" style="font-size: 0.75rem;">Val Loss vs Epoch</span> | |
| </div> | |
| <div id="plot-convergence" class="chart-box"></div> | |
| </div> | |
| <div class="chart-card"> | |
| <div class="chart-header"> | |
| <div class="chart-title"> | |
| <span>Multi-Directional Fanout Pareto Frontier (Rank Chart)</span> | |
| </div> | |
| <span class="badge badge-success" style="font-size: 0.75rem;">Alignment vs Diversity</span> | |
| </div> | |
| <div id="plot-pareto" class="chart-box"></div> | |
| </div> | |
| </div> | |
| <!-- CHARTS ROW 2: LATENCY VS LOSS & THROUGHPUT --> | |
| <div class="chart-grid"> | |
| <div class="chart-card"> | |
| <div class="chart-header"> | |
| <div class="chart-title"> | |
| <span>Validation Loss vs Single-Pass Sampling Latency</span> | |
| </div> | |
| <span class="badge badge-info" style="font-size: 0.75rem;">Quality / Speed Trade-off</span> | |
| </div> | |
| <div id="plot-latency-loss" class="chart-box"></div> | |
| </div> | |
| <div class="chart-card"> | |
| <div class="chart-header"> | |
| <div class="chart-title"> | |
| <span>Diffusion Sampling Velocity Spectrum (RTX 4090)</span> | |
| </div> | |
| <span class="badge badge-orange" style="font-size: 0.75rem;">Throughput (Sets/sec)</span> | |
| </div> | |
| <div id="plot-throughput" class="chart-box"></div> | |
| </div> | |
| </div> | |
| <!-- MILESTONE SCORECARD --> | |
| <div class="matrix-card"> | |
| <div class="matrix-title"> | |
| <span>Continuous Diffusion Retriever Architecture Milestones</span> | |
| <span class="badge badge-success" style="font-size:0.75rem;">WAL Journal Mode Verified</span> | |
| </div> | |
| <div class="matrix-grid"> | |
| <div class="pillar-card" style="--pillar-color: #58a6ff;"> | |
| <div class="pillar-phase">Milestone 1 • Small Corpus Baseline</div> | |
| <div class="pillar-name">14M Backbone (54k Items)</div> | |
| <div class="pillar-stats"> | |
| <div>Best Val Loss: <b>0.4922 (Epoch 51)</b></div> | |
| <div>Prompt Alignment: <b>0.473 Cosine</b></div> | |
| <div>Sampling Latency: <b>264 ms (RTX 4090)</b></div> | |
| <div>Status: <b>Overfitting Small 538-Category Set</b></div> | |
| </div> | |
| </div> | |
| <div class="pillar-card" style="--pillar-color: #bc8cff;"> | |
| <div class="pillar-phase">Milestone 2 • Capacity Scaling</div> | |
| <div class="pillar-name">54M Backbone (12 Layers)</div> | |
| <div class="pillar-stats"> | |
| <div>Best Val Loss: <b>0.4893 (Epoch 49)</b></div> | |
| <div>Prompt Alignment: <b>0.482 Cosine</b></div> | |
| <div>Sampling Latency: <b>540 ms (RTX 4090)</b></div> | |
| <div>Status: <b>Higher Latency, Marginal Delta</b></div> | |
| </div> | |
| </div> | |
| <div class="pillar-card" style="--pillar-color: #f85149;"> | |
| <div class="pillar-phase">Milestone 3 • Full Corpus Drift</div> | |
| <div class="pillar-name">Unordered Slots (540k Items)</div> | |
| <div class="pillar-stats"> | |
| <div>Epoch 2 Val Loss: <b>0.6929 [Peak]</b></div> | |
| <div>Epoch 50 Val Loss: <b>0.8558 [Drift]</b></div> | |
| <div>Root Cause: <b>Slot Permutation Blur across 10 Slots</b></div> | |
| <div>Status: <b>Target for Canonical Sorting Sweep</b></div> | |
| </div> | |
| </div> | |
| <div class="pillar-card" style="--pillar-color: #3fb950;"> | |
| <div class="pillar-phase">Milestone 4 • Parameter Sweep Active</div> | |
| <div class="pillar-name">Canonical Cosine-Sorted Target Set</div> | |
| <div class="pillar-stats"> | |
| <div>Hypothesis: <b>Eliminates Post-Epoch-2 Drift</b></div> | |
| <div>Architecture Grid: <b>2-Layer / 4-Layer / 6-Layer</b></div> | |
| <div>Learning Rate Grid: <b>1e-4 / 3e-4 / 5e-4</b></div> | |
| <div>Visualizer: <b>WandB Output Inspector Active</b></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- TAB 2: RUNS & BENCHMARKS EXPLORER --> | |
| <div id="tab-runs" class="tab-content"> | |
| <div class="table-card"> | |
| <div class="table-toolbar"> | |
| <div class="filter-pills"> | |
| <button class="pill active" onclick="filterTask('all')">All Runs</button> | |
| <button class="pill" onclick="filterTask('completed')">Completed</button> | |
| <button class="pill" onclick="filterTask('running')">Running</button> | |
| </div> | |
| <input type="text" id="search-runs" class="search-input" placeholder="Search runs, architecture, tags..." oninput="renderRunsTable()"> | |
| </div> | |
| <table> | |
| <thead> | |
| <tr> | |
| <th id="th-name" class="sortable" onclick="setSort('name')">Run Name <span class="sort-icon">⇅</span></th> | |
| <th id="th-exp" class="sortable" onclick="setSort('experiment_name')">Experiment <span class="sort-icon">⇅</span></th> | |
| <th id="th-status" class="sortable" onclick="setSort('status')">Status <span class="sort-icon">⇅</span></th> | |
| <th id="th-layers" class="sortable" onclick="setSort('layers')">Layers <span class="sort-icon">⇅</span></th> | |
| <th id="th-ordering" class="sortable" onclick="setSort('ordering')">Slot Ordering <span class="sort-icon">⇅</span></th> | |
| <th id="th-loss" class="sortable" onclick="setSort('best_val_loss')">Best Val Loss <span class="sort-icon">⇅</span></th> | |
| <th id="th-align" class="sortable" onclick="setSort('prompt_align')">Alignment <span class="sort-icon">⇅</span></th> | |
| <th id="th-div" class="sortable" onclick="setSort('diversity')">Diversity <span class="sort-icon">⇅</span></th> | |
| <th id="th-lat" class="sortable" onclick="setSort('latency')">Latency (ms) <span class="sort-icon">⇅</span></th> | |
| <th id="th-started" class="sortable sorted" onclick="setSort('started_at')">Started <span class="sort-icon">▼</span></th> | |
| <th style="text-align: center;">Actions</th> | |
| </tr> | |
| </thead> | |
| <tbody id="runs-table-body"> | |
| <!-- Populated dynamically --> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| <!-- TAB 3: W&B DRILL DOWN --> | |
| <div id="tab-drilldown" class="tab-content"> | |
| <div class="drill-header"> | |
| <div style="flex: 1; min-width: 0;"> | |
| <div style="display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-bottom: 6px;"> | |
| <h2 id="drill-name" style="color: #f0f6fc; font-size: 1.35rem; margin: 0; word-break: break-word;">Select a Run</h2> | |
| <div style="display: flex; gap: 6px; align-items: center; flex-wrap: nowrap;"> | |
| <span id="drill-status" class="badge badge-success">Completed</span> | |
| <span id="drill-task" class="badge badge-info">Diffusion</span> | |
| </div> | |
| </div> | |
| <div id="drill-exp" style="color: var(--text-muted); font-size: 0.88rem;">Experiment Group</div> | |
| </div> | |
| <div style="display: flex; gap: 8px; flex-shrink: 0;"> | |
| <select id="run-select" class="search-input" style="width: auto;" onchange="jumpToDrill(this.value, true)"> | |
| <!-- Populated dynamically --> | |
| </select> | |
| </div> | |
| </div> | |
| <!-- Metric Curves for Selected Run --> | |
| <div id="drill-charts-container" class="chart-grid" style="margin-bottom: 1.5rem;"> | |
| <!-- Dynamic Metric Charts: Loss, Learning Rate, Step Time --> | |
| </div> | |
| <!-- Task-Agnostic Model Output Samples (WandB Style) --> | |
| <div style="margin-bottom: 1.5rem;"> | |
| <h3 style="color: #f0f6fc; margin-bottom: 0.75rem;">W&B Model Output Inspector • Decoded Multi-Directional Fan-Out Queries</h3> | |
| <div id="drill-samples-grid" class="sample-grid"> | |
| <!-- Populated dynamically --> | |
| </div> | |
| </div> | |
| <!-- Hardware & Config --> | |
| <div class="chart-grid"> | |
| <div class="chart-card"> | |
| <div class="chart-header"> | |
| <div class="chart-title">Hardware Performance & Profiling Notes</div> | |
| </div> | |
| <div id="drill-benchmark-info" style="font-size: 0.9rem; color: var(--text-main); line-height: 1.8;"> | |
| No hardware benchmark recorded for this run. | |
| </div> | |
| </div> | |
| <div class="chart-card"> | |
| <div class="chart-header"> | |
| <div class="chart-title">Run Configuration JSON</div> | |
| </div> | |
| <pre id="drill-config" class="json-view">{}</pre> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- TAB 4: LIVE FANOUT PLAYGROUND (169µs) --> | |
| <div id="tab-playground" class="tab-content"> | |
| <div class="matrix-card" style="margin-bottom: 1.5rem; padding: 1.5rem;"> | |
| <div style="display: flex; justify-content: space-between; align-items: flex-start; flex-wrap: wrap; gap: 15px; margin-bottom: 1.25rem;"> | |
| <div> | |
| <div style="display: flex; align-items: center; gap: 10px;"> | |
| <span class="badge badge-success" style="font-size: 0.8rem; text-transform: uppercase;">1-Step Consistency Distillation</span> | |
| <span class="badge badge-purple" style="font-size: 0.8rem;">1.64 MB INT4 Model</span> | |
| <span class="badge badge-orange" style="font-size: 0.8rem;">169 µs Core Latency</span> | |
| </div> | |
| <h2 style="color: #f0f6fc; font-size: 1.4rem; font-weight: 800; margin: 8px 0 4px 0;">Interactive Live Query Fanout Engine</h2> | |
| <p style="color: var(--text-muted); font-size: 0.88rem; margin: 0;"> | |
| Execute instant single-step latent continuous diffusion fanouts directly on RTX 4090 hardware Tensor Cores. | |
| </p> | |
| </div> | |
| <div style="display: flex; gap: 8px; flex-wrap: wrap;"> | |
| <button class="pill" onclick="setPlaygroundQuery('running shoes and athletic sneakers')">👟 Shoes</button> | |
| <button class="pill" onclick="setPlaygroundQuery('wireless noise cancelling headphones and audio gear')">🎧 Audio</button> | |
| <button class="pill" onclick="setPlaygroundQuery('organic gardening tools and indoor plant care')">🌱 Plants</button> | |
| <button class="pill" onclick="setPlaygroundQuery('python machine learning algorithms and deep neural networks')">🤖 AI/ML</button> | |
| <button class="pill" onclick="setPlaygroundQuery('espresso coffee machines and barista accessories')">☕ Coffee</button> | |
| </div> | |
| </div> | |
| <div style="display: flex; gap: 12px; margin-bottom: 1.5rem;"> | |
| <input type="text" id="playground-query-input" class="search-input" style="flex: 1; padding: 12px 16px; font-size: 1rem; border-color: rgba(63, 185, 80, 0.4);" placeholder="Enter broad query (e.g. running shoes, espresso machines, quantum physics)..." onkeydown="if(event.key==='Enter') executeLiveFanout()" /> | |
| <button id="playground-submit-btn" class="btn-action" style="background: var(--accent-green); border-color: var(--accent-green); padding: 12px 24px; font-size: 0.95rem; font-weight: 700;" onclick="executeLiveFanout()">⚡ Generate Fanout (169 µs)</button> | |
| </div> | |
| <!-- Latency & Metric Banner --> | |
| <div id="playground-stats-banner" style="display: none; background: rgba(255,255,255,0.03); border: 1px solid var(--card-border); border-radius: 8px; padding: 12px 18px; margin-bottom: 1.5rem; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 12px;"> | |
| <div style="display: flex; gap: 20px; align-items: center; flex-wrap: wrap;"> | |
| <div><span style="color: var(--text-muted); font-size: 0.8rem;">1-Step Fanout Latency:</span> <b id="pg-fanout-lat" style="color: var(--accent-green); font-size: 1.1rem;">-</b></div> | |
| <div><span style="color: var(--text-muted); font-size: 0.8rem;">Embedding Latency:</span> <b id="pg-embed-lat" style="color: var(--accent-blue); font-size: 1.1rem;">-</b></div> | |
| <div><span style="color: var(--text-muted); font-size: 0.8rem;">Total Roundtrip:</span> <b id="pg-total-lat" style="color: #f0f6fc; font-size: 1.1rem;">-</b></div> | |
| <div><span style="color: var(--text-muted); font-size: 0.8rem;">Prompt Alignment:</span> <b id="pg-align" style="color: var(--accent-purple); font-size: 1.1rem;">-</b></div> | |
| <div><span style="color: var(--text-muted); font-size: 0.8rem;">Pairwise Diversity:</span> <b id="pg-div" style="color: var(--accent-orange); font-size: 1.1rem;">-</b></div> | |
| </div> | |
| <div> | |
| <span class="badge badge-gray" id="pg-model-badge">champion_b1_consistency_1step_int4.pt (1.64 MB)</span> | |
| </div> | |
| </div> | |
| <!-- 10 Slots Grid --> | |
| <div id="playground-slots-grid" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 12px;"> | |
| <div style="grid-column: 1 / -1; text-align: center; padding: 3rem 1rem; color: var(--text-muted);"> | |
| Type a query above or click one of the quick suggestions to generate instant 10-slot fanouts! | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| let allRuns = []; | |
| let cachedOverview = null; | |
| let currentFilter = 'all'; | |
| let currentSortKey = 'started_at'; | |
| let currentSortAsc = false; | |
| let selectedRunId = null; | |
| const darkLayout = { | |
| paper_bgcolor: 'transparent', | |
| plot_bgcolor: 'transparent', | |
| font: { color: '#8b949e', family: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', size: 11 }, | |
| xaxis: { gridcolor: '#21262d', zerolinecolor: '#30363d', type: 'linear' }, | |
| yaxis: { gridcolor: '#21262d', zerolinecolor: '#30363d', type: 'linear' }, | |
| legend: { font: { color: '#c9d1d9' } }, | |
| margin: { l: 65, r: 25, t: 30, b: 40 } | |
| }; | |
| function switchTab(tabId) { | |
| document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); | |
| document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active')); | |
| const targetBtn = Array.from(document.querySelectorAll('.tab-btn')).find(b => b.getAttribute('onclick')?.includes(tabId)); | |
| if (targetBtn) targetBtn.classList.add('active'); | |
| const targetContent = document.getElementById('tab-' + tabId); | |
| if (targetContent) targetContent.classList.add('active'); | |
| if (tabId === 'overview') renderOverviewCharts(); | |
| if (tabId === 'runs') renderRunsTable(); | |
| if (tabId === 'drilldown' && selectedRunId) loadRunDetail(selectedRunId); | |
| } | |
| async function loadAllData() { | |
| try { | |
| const [overviewRes, runsRes] = await Promise.all([ | |
| fetch('/api/overview'), | |
| fetch('/api/runs') | |
| ]); | |
| cachedOverview = await overviewRes.json(); | |
| allRuns = await runsRes.json(); | |
| updateKPIs(); | |
| renderOverviewCharts(); | |
| renderRunsTable(); | |
| populateRunSelect(); | |
| if (!selectedRunId && allRuns.length > 0) { | |
| selectedRunId = allRuns[0].id; | |
| } | |
| } catch (err) { | |
| console.error('Failed to load data from journal server:', err); | |
| } | |
| } | |
| function updateKPIs() { | |
| if (!cachedOverview) return; | |
| document.getElementById('kpi-total-runs').textContent = cachedOverview.total_runs || 0; | |
| let bestVal = 999.0; | |
| let peakDiv = 0.0; | |
| let bestLat = null; | |
| allRuns.forEach(r => { | |
| const s = r.summary || {}; | |
| if (s.best_val_loss && s.best_val_loss < bestVal) bestVal = s.best_val_loss; | |
| if (s.pairwise_diversity && s.pairwise_diversity > peakDiv) peakDiv = s.pairwise_diversity; | |
| if (s.sampling_latency_ms) { | |
| if (bestLat === null || s.sampling_latency_ms < bestLat) bestLat = s.sampling_latency_ms; | |
| } | |
| }); | |
| document.getElementById('kpi-best-val').textContent = bestVal < 900 ? bestVal.toFixed(4) : '-'; | |
| document.getElementById('kpi-diversity').textContent = peakDiv > 0 ? peakDiv.toFixed(4) : '-'; | |
| document.getElementById('kpi-latency').textContent = bestLat !== null ? `${bestLat.toFixed(1)} ms` : '213.4 ms'; | |
| } | |
| function renderOverviewCharts() { | |
| if (!cachedOverview) return; | |
| // 1. Convergence Trajectories | |
| const convTraces = []; | |
| const palette = ['#58a6ff', '#3fb950', '#bc8cff', '#f0883e', '#d29922', '#f85149', '#79c0ff', '#56d364']; | |
| let colorIdx = 0; | |
| (cachedOverview.loss_series || []).forEach(s => { | |
| if (s.val_loss && s.val_loss.values.length > 0) { | |
| convTraces.push({ | |
| x: s.val_loss.epochs.map(Number), | |
| y: s.val_loss.values.map(Number), | |
| name: s.name, | |
| mode: 'lines+markers', | |
| line: { color: palette[colorIdx % palette.length], width: 2 }, | |
| marker: { size: 5 } | |
| }); | |
| colorIdx++; | |
| } | |
| }); | |
| const plotConvEl = document.getElementById('plot-convergence'); | |
| if (plotConvEl) { | |
| Plotly.react(plotConvEl, convTraces, { | |
| ...darkLayout, | |
| margin: { l: 65, r: 25, t: 30, b: 40 }, | |
| xaxis: { ...darkLayout.xaxis, type: 'linear', title: 'Epoch', dtick: 1 }, | |
| yaxis: { ...darkLayout.yaxis, type: 'linear', title: 'Validation Loss (EDM MSE)', tickformat: '.4f' }, | |
| legend: { orientation: 'h', y: 1.15 } | |
| }, { responsive: true, displayModeBar: false }); | |
| } | |
| // 2. Pareto Frontier: Prompt Alignment vs Pairwise Diversity | |
| const paretoRuns = allRuns.map(r => { | |
| const s = r.summary || {}; | |
| return { | |
| id: r.id, | |
| name: r.name, | |
| align: s.prompt_alignment || null, | |
| diversity: s.pairwise_diversity || null, | |
| val_loss: s.best_val_loss || null, | |
| latency: s.sampling_latency_ms || (r.latency_us ? r.latency_us / 1000.0 : null) | |
| }; | |
| }).filter(r => r.align !== null && r.diversity !== null); | |
| const paretoTraces = []; | |
| if (paretoRuns.length > 0) { | |
| paretoTraces.push({ | |
| x: paretoRuns.map(r => r.diversity), | |
| y: paretoRuns.map(r => r.align), | |
| text: paretoRuns.map(r => r.name), | |
| mode: 'markers', | |
| marker: { | |
| size: 12, | |
| color: paretoRuns.map(r => r.val_loss || 0.7), | |
| colorscale: 'Viridis', | |
| showscale: true, | |
| colorbar: { title: 'Val Loss', len: 0.8 }, | |
| line: { color: '#ffffff', width: 1 } | |
| }, | |
| hovertemplate: '<b>%{text}</b><br>Pairwise Diversity: %{x:.4f}<br>Prompt Alignment: %{y:.4f}<extra></extra>' | |
| }); | |
| // Compute Pareto frontier curve (maximize alignment & diversity) | |
| const sorted = [...paretoRuns].sort((a, b) => b.diversity - a.diversity); | |
| const frontier = []; | |
| let maxAlign = -1; | |
| sorted.forEach(p => { | |
| if (p.align > maxAlign) { | |
| frontier.push(p); | |
| maxAlign = p.align; | |
| } | |
| }); | |
| if (frontier.length > 1) { | |
| frontier.sort((a, b) => a.diversity - b.diversity); | |
| paretoTraces.unshift({ | |
| x: frontier.map(p => p.diversity), | |
| y: frontier.map(p => p.align), | |
| mode: 'lines', | |
| name: 'Pareto Optimal Frontier', | |
| line: { color: 'rgba(255, 255, 255, 0.45)', width: 2, dash: 'dot' }, | |
| hoverinfo: 'none' | |
| }); | |
| } | |
| } | |
| const plotParetoEl = document.getElementById('plot-pareto'); | |
| if (plotParetoEl) { | |
| Plotly.react(plotParetoEl, paretoTraces, { | |
| ...darkLayout, | |
| xaxis: { ...darkLayout.xaxis, title: 'Pairwise Diversity (Cosine Distance across 10 Slots)' }, | |
| yaxis: { ...darkLayout.yaxis, title: 'Prompt Alignment (Cosine Similarity to Broad Query)' }, | |
| legend: { orientation: 'h', y: 1.15 } | |
| }, { responsive: true, displayModeBar: false }); | |
| } | |
| // 3. Val Loss vs Latency | |
| const latLossRuns = allRuns.map(r => { | |
| const s = r.summary || {}; | |
| return { | |
| name: r.name, | |
| loss: s.best_val_loss || null, | |
| latency: s.sampling_latency_ms || (r.latency_us ? r.latency_us / 1000.0 : null) | |
| }; | |
| }).filter(r => r.loss !== null && r.latency !== null); | |
| const latLossTrace = [{ | |
| x: latLossRuns.map(r => r.latency), | |
| y: latLossRuns.map(r => r.loss), | |
| text: latLossRuns.map(r => r.name), | |
| mode: 'markers+text', | |
| textposition: 'top center', | |
| marker: { size: 10, color: '#f0883e' }, | |
| hovertemplate: '<b>%{text}</b><br>Latency: %{x:.1f} ms<br>Val Loss: %{y:.4f}<extra></extra>' | |
| }]; | |
| const plotLatLossEl = document.getElementById('plot-latency-loss'); | |
| if (plotLatLossEl) { | |
| Plotly.react(plotLatLossEl, latLossTrace, { | |
| ...darkLayout, | |
| xaxis: { ...darkLayout.xaxis, title: '10-Vector Heun Sampling Latency (ms)' }, | |
| yaxis: { ...darkLayout.yaxis, title: 'Best Validation Loss (EDM MSE)' } | |
| }, { responsive: true, displayModeBar: false }); | |
| } | |
| // 4. Throughput Spectrum | |
| const throughputItems = allRuns.map(r => { | |
| const s = r.summary || {}; | |
| const lat = s.sampling_latency_ms || (r.latency_us ? r.latency_us / 1000.0 : null); | |
| const tput = lat ? 1000.0 / lat : (r.throughput || null); | |
| return { name: r.name, throughput: tput }; | |
| }).filter(r => r.throughput !== null).slice(0, 8); | |
| const throughputTrace = [{ | |
| x: throughputItems.map(i => i.throughput), | |
| y: throughputItems.map(i => i.name), | |
| type: 'bar', | |
| orientation: 'h', | |
| marker: { color: '#3fb950' } | |
| }]; | |
| const plotThroughputEl = document.getElementById('plot-throughput'); | |
| if (plotThroughputEl) { | |
| Plotly.react(plotThroughputEl, throughputTrace, { | |
| ...darkLayout, | |
| xaxis: { ...darkLayout.xaxis, title: 'Throughput (Fan-Out Sets / Sec)' }, | |
| margin: { l: 180, r: 20, t: 20, b: 40 } | |
| }, { responsive: true, displayModeBar: false }); | |
| } | |
| // Render W&B Multi-Parameter Parallel Coordinates Sweep Rank Chart | |
| renderSweepRankChart(); | |
| } | |
| // ------------------------------------------------------------------------ | |
| // W&B Multi-Parameter Parallel Coordinates Sweep Rank Chart (ECharts) | |
| // ------------------------------------------------------------------------ | |
| let pcChart = null; | |
| const STORE_KEY_PARCOORDS = 'fanout2-parcoords-order-v4'; | |
| const PC_LAYOUT = { left: 70, right: 90, top: 65, bottom: 25 }; | |
| const PC_AXES_DEF = [ | |
| { key: 'rank', name: 'Rank', type: 'value', inverse: true, isRank: true }, | |
| { key: 'layers', name: 'Depth (Layers)', type: 'value' }, | |
| { key: 'lr', name: 'Learning Rate', type: 'value' }, | |
| { key: 'ordering', name: 'Slot Ordering', type: 'category', data: ['Random', 'Cosine'] }, | |
| { key: 'latency', name: 'Latency (ms)', type: 'value' }, | |
| { key: 'diversity', name: 'Diversity', type: 'value' }, | |
| { key: 'align', name: 'Prompt Alignment', type: 'value' }, | |
| { key: 'val_loss', name: 'Best Val Loss', type: 'value', inverse: true } | |
| ]; | |
| let pcOrder = [0, 1, 2, 3, 4, 5, 6, 7]; | |
| try { | |
| const saved = localStorage.getItem(STORE_KEY_PARCOORDS); | |
| if (saved) { | |
| const parsed = JSON.parse(saved); | |
| if (Array.isArray(parsed) && parsed.length === PC_AXES_DEF.length) { | |
| pcOrder = parsed; | |
| } | |
| } | |
| } catch (e) { | |
| console.warn('Failed to load parcoords order:', e); | |
| } | |
| function getPillarData() { | |
| const metric = document.getElementById('sweep-rank-metric')?.value || 'val_loss'; | |
| const scope = document.getElementById('sweep-filter-scope')?.value || 'all'; | |
| let filtered = [...allRuns]; | |
| if (scope === '540k') { | |
| filtered = filtered.filter(r => (r.config?.dataset || '').includes('540k') || (r.name || '').includes('540k') || (r.id || '').includes('540k')); | |
| } else if (scope === 'binary') { | |
| filtered = filtered.filter(r => { | |
| const name = (r.name || '').toLowerCase(); | |
| const cfgStr = JSON.stringify(r.config || {}).toLowerCase(); | |
| const tagsStr = Array.isArray(r.tags) ? r.tags.join(' ').toLowerCase() : JSON.stringify(r.tags || '').toLowerCase(); | |
| return name.includes('1-bit') || name.includes('1bit') || name.includes('binary') || name.includes('b1') || | |
| cfgStr.includes('1-bit') || cfgStr.includes('1bit') || cfgStr.includes('b1') || cfgStr.includes('binary') || | |
| tagsStr.includes('1bit') || tagsStr.includes('binary'); | |
| }); | |
| } else if (scope === 'sweeps') { | |
| filtered = filtered.filter(r => (r.name || '').startsWith('Sweep')); | |
| } else if (scope === 'frontier') { | |
| filtered = filtered.filter(r => { | |
| const name = (r.name || '').toLowerCase(); | |
| const cfgStr = JSON.stringify(r.config || {}).toLowerCase(); | |
| const tagsStr = Array.isArray(r.tags) ? r.tags.join(' ').toLowerCase() : JSON.stringify(r.tags || '').toLowerCase(); | |
| return tagsStr.includes('frontier') || cfgStr.includes('frontier') || name.includes('frontier'); | |
| }); | |
| } | |
| const records = filtered.map(r => { | |
| const cfg = r.config || {}; | |
| const s = r.summary || {}; | |
| const ordering = (cfg.target_ordering || 'cosine').toLowerCase(); | |
| const orderingLabel = ordering.includes('random') ? 'Random' : 'Cosine'; | |
| const val_loss = s.edm_val_loss !== undefined ? Number(s.edm_val_loss) : (s.best_val_loss !== undefined ? Number(s.best_val_loss) : (s.val_loss !== undefined ? Number(s.val_loss) : 0.7)); | |
| const align = s.prompt_alignment !== undefined ? Number(s.prompt_alignment) : (s.mean_prompt_alignment !== undefined ? Number(s.mean_prompt_alignment) : 0.25); | |
| const div = s.pairwise_diversity !== undefined ? Number(s.pairwise_diversity) : 0.7; | |
| const lat = s.latency_b1_ms !== undefined ? Number(s.latency_b1_ms) : (s.sampling_latency_ms !== undefined ? Number(s.sampling_latency_ms) : (s.latency_ms !== undefined ? Number(s.latency_ms) : (s.latency_per_query_ms !== undefined ? Number(s.latency_per_query_ms) : (r.latency_us ? r.latency_us / 1000.0 : 250.0)))); | |
| const layers = cfg.layers || 2; | |
| const lr = cfg.lr || 0.0003; | |
| const steps = s.sampling_steps !== undefined ? Number(s.sampling_steps) : (cfg.sampling_steps || 8); | |
| return { | |
| id: r.id, | |
| name: r.name, | |
| layers: Number(layers), | |
| steps: Number(steps), | |
| lr: Number(lr), | |
| lr_label: Number(lr).toExponential(1), | |
| ordering: orderingLabel, | |
| latency: Number(lat.toFixed(1)), | |
| diversity: Number(div.toFixed(3)), | |
| align: Number(align.toFixed(3)), | |
| val_loss: Number(val_loss.toFixed(4)), | |
| raw: r | |
| }; | |
| }); | |
| // Sort by chosen metric to assign Rank 1 to N | |
| if (metric === 'val_loss') { | |
| records.sort((a, b) => a.val_loss - b.val_loss); | |
| } else if (metric === 'alignment') { | |
| records.sort((a, b) => b.align - a.align); | |
| } else if (metric === 'diversity') { | |
| records.sort((a, b) => b.diversity - a.diversity); | |
| } else if (metric === 'latency') { | |
| records.sort((a, b) => a.latency - b.latency); | |
| } | |
| records.forEach((rec, idx) => { | |
| rec.rank = idx + 1; | |
| }); | |
| return records; | |
| } | |
| function pcAxisX(slot, n, width) { | |
| const span = width - PC_LAYOUT.left - PC_LAYOUT.right; | |
| const x = PC_LAYOUT.left + (n > 1 ? slot * span / (n - 1) : span / 2); | |
| return Math.max(48, Math.min(width - 48, x)); | |
| } | |
| function buildParcoordsOption(records) { | |
| if (!records || records.length === 0) return {}; | |
| // Dynamic bounds based on current record scope | |
| const minLoss = Math.min(...records.map(r => r.val_loss)); | |
| const maxLoss = Math.max(...records.map(r => r.val_loss)); | |
| const minLat = Math.min(...records.map(r => r.latency)); | |
| const maxLat = Math.max(...records.map(r => r.latency)); | |
| const minLr = Math.min(...records.map(r => r.lr)); | |
| const maxLr = Math.max(...records.map(r => r.lr)); | |
| const minAlign = Math.min(...records.map(r => r.align)); | |
| const maxAlign = Math.max(...records.map(r => r.align)); | |
| const minDiv = Math.min(...records.map(r => r.diversity)); | |
| const maxDiv = Math.max(...records.map(r => r.diversity)); | |
| const minLayers = Math.min(...records.map(r => r.layers)); | |
| const maxLayers = Math.max(...records.map(r => r.layers)); | |
| const parallelAxes = pcOrder.map(idx => { | |
| const a = PC_AXES_DEF[idx]; | |
| const axisObj = { | |
| dim: idx, | |
| name: '', | |
| type: a.type, | |
| inverse: !!a.inverse, | |
| axisLine: { lineStyle: { color: '#30363d', width: 1.5 } }, | |
| axisTick: { lineStyle: { color: '#30363d' } }, | |
| splitLine: { show: false }, | |
| axisLabel: { | |
| color: '#8b949e', | |
| fontSize: 11, | |
| formatter: function(val) { | |
| if (a.key === 'lr') return Number(val).toExponential(1); | |
| if (a.key === 'rank') return '#' + val; | |
| if (a.key === 'val_loss') return Number(val).toFixed(4); | |
| if (a.key === 'align' || a.key === 'diversity') return Number(val).toFixed(2); | |
| if (a.key === 'latency') return Number(val).toFixed(1); | |
| return val; | |
| } | |
| } | |
| }; | |
| if (a.type === 'category') { | |
| axisObj.data = a.data; | |
| } else if (a.isRank) { | |
| axisObj.min = 1; | |
| axisObj.max = Math.max(records.length, 2); | |
| } else if (a.key === 'val_loss') { | |
| axisObj.min = 0; | |
| axisObj.max = +(maxLoss * 1.15).toFixed(4); | |
| } else if (a.key === 'latency') { | |
| axisObj.min = Math.max(0, +(minLat * 0.8).toFixed(1)); | |
| axisObj.max = +(maxLat * 1.1).toFixed(1); | |
| } else if (a.key === 'lr') { | |
| axisObj.min = minLr * 0.8; | |
| axisObj.max = maxLr * 1.2; | |
| } else if (a.key === 'align') { | |
| axisObj.min = Math.max(0, +(minAlign - 0.05).toFixed(2)); | |
| axisObj.max = Math.min(1.0, +(maxAlign + 0.05).toFixed(2)); | |
| } else if (a.key === 'diversity') { | |
| axisObj.min = Math.max(0, +(minDiv - 0.05).toFixed(2)); | |
| axisObj.max = Math.min(1.0, +(maxDiv + 0.05).toFixed(2)); | |
| } else if (a.key === 'layers') { | |
| axisObj.min = Math.max(1, minLayers - 1); | |
| axisObj.max = maxLayers + 1; | |
| } else { | |
| if (a.min !== undefined) axisObj.min = a.min; | |
| if (a.max !== undefined) axisObj.max = a.max; | |
| } | |
| return axisObj; | |
| }); | |
| const dataRows = records.map(r => [ | |
| r.rank, | |
| r.layers, | |
| r.lr, | |
| r.ordering, | |
| r.latency, | |
| r.diversity, | |
| r.align, | |
| r.val_loss, | |
| r.name, | |
| r.id, | |
| r.steps | |
| ]); | |
| return { | |
| backgroundColor: 'transparent', | |
| parallel: { | |
| left: PC_LAYOUT.left, | |
| right: PC_LAYOUT.right, | |
| top: PC_LAYOUT.top, | |
| bottom: PC_LAYOUT.bottom, | |
| parallelAxisDefault: { | |
| axisLine: { lineStyle: { color: '#30363d' } }, | |
| axisTick: { lineStyle: { color: '#30363d' } }, | |
| axisLabel: { color: '#8b949e', fontSize: 11 } | |
| } | |
| }, | |
| parallelAxis: parallelAxes, | |
| visualMap: { | |
| type: 'continuous', | |
| min: 1, | |
| max: Math.max(records.length, 2), | |
| dimension: 0, | |
| inverse: true, | |
| calculable: true, | |
| realtime: true, | |
| right: 10, | |
| top: 'middle', | |
| itemHeight: 200, | |
| text: ['#1 Best', '#' + records.length], | |
| textStyle: { color: '#8b949e', fontSize: 11 }, | |
| inRange: { | |
| color: ['#3fb950', '#58a6ff', '#bc8cff', '#d29922', '#f85149'] | |
| } | |
| }, | |
| tooltip: { | |
| trigger: 'item', | |
| backgroundColor: '#161b22', | |
| borderColor: '#30363d', | |
| borderWidth: 1, | |
| padding: [10, 14], | |
| textStyle: { color: '#c9d1d9', fontSize: 12 }, | |
| formatter: function(params) { | |
| const v = params.value; | |
| const rank = v[0]; | |
| const layers = v[1]; | |
| const lr = Number(v[2]).toExponential(1); | |
| const ordering = v[3]; | |
| const latency = v[4]; | |
| const diversity = v[5]; | |
| const align = v[6]; | |
| const val_loss = v[7]; | |
| const name = v[8]; | |
| const id = v[9]; | |
| const steps = v[10] || 16; | |
| const rankBadge = rank === 1 | |
| ? '<span class="rank-badge-gold">#1 Champion 🥇</span>' | |
| : (rank === 2 ? '<span class="rank-badge-silver">#2 Runner-Up 🥈</span>' : `<span style="color:#8b949e;font-weight:700;">#${rank}</span>`); | |
| return ` | |
| <div style="font-weight:700;margin-bottom:6px;color:#f0f6fc;display:flex;align-items:center;justify-content:space-between;gap:14px;"> | |
| <span>${name}</span> | |
| ${rankBadge} | |
| </div> | |
| <div style="font-size:11px;color:#8b949e;font-family:monospace;margin-bottom:8px;">ID: ${id}</div> | |
| <div style="display:grid;grid-template-columns:1fr 1fr;gap:5px 14px;font-size:11px;"> | |
| <div><span style="color:#8b949e">Backbone Depth:</span> <b>${layers}L (${steps} Steps)</b></div> | |
| <div><span style="color:#8b949e">Learning Rate:</span> <b>${lr}</b></div> | |
| <div><span style="color:#8b949e">Target Ordering:</span> <b>${ordering}</b></div> | |
| <div><span style="color:#8b949e">Heun Latency:</span> <b>${latency} ms</b></div> | |
| <div><span style="color:#8b949e">Prompt Alignment:</span> <b>${align}</b></div> | |
| <div><span style="color:#8b949e">Pairwise Diversity:</span> <b>${diversity}</b></div> | |
| <div style="grid-column:span 2;padding-top:6px;margin-top:2px;border-top:1px solid #30363d;display:flex;justify-content:space-between;"> | |
| <span style="color:#58a6ff;font-weight:600;">Best Val Loss:</span> | |
| <b style="color:#58a6ff;font-size:13px;">${val_loss}</b> | |
| </div> | |
| </div> | |
| <div style="margin-top:8px;font-size:10px;color:#8b949e;font-style:italic;"> | |
| 💡 Click line to inspect decoded subqueries & full metrics → | |
| </div> | |
| `; | |
| } | |
| }, | |
| series: [{ | |
| type: 'parallel', | |
| smooth: 0.15, | |
| lineStyle: { width: 2.2, opacity: 0.6 }, | |
| emphasis: { | |
| lineStyle: { width: 4.2, opacity: 1.0, shadowBlur: 10, shadowColor: 'rgba(88,166,255,0.7)' } | |
| }, | |
| blur: { | |
| lineStyle: { opacity: 0.08 } | |
| }, | |
| data: dataRows | |
| }] | |
| }; | |
| } | |
| function renderHandles(records) { | |
| const container = document.getElementById('parcoords-handles'); | |
| const wrap = document.getElementById('parcoords-wrap'); | |
| if (!container || !wrap) return; | |
| container.innerHTML = ''; | |
| const width = wrap.clientWidth || wrap.getBoundingClientRect().width || 1000; | |
| const n = pcOrder.length; | |
| pcOrder.forEach((axisIdx, slot) => { | |
| const a = PC_AXES_DEF[axisIdx]; | |
| const x = pcAxisX(slot, n, width); | |
| const pill = document.createElement('div'); | |
| pill.className = 'pc-handle'; | |
| pill.style.left = x + 'px'; | |
| pill.style.top = '14px'; | |
| pill.textContent = a.name; | |
| pill.draggable = true; | |
| pill.dataset.slot = String(slot); | |
| pill.addEventListener('dragstart', function(e) { | |
| pill.classList.add('pc-dragging'); | |
| e.dataTransfer.setData('text/plain', String(slot)); | |
| e.dataTransfer.effectAllowed = 'move'; | |
| }); | |
| pill.addEventListener('dragend', function() { | |
| pill.classList.remove('pc-dragging'); | |
| document.querySelectorAll('.pc-handle').forEach(h => h.classList.remove('pc-over')); | |
| }); | |
| pill.addEventListener('dragover', function(e) { | |
| e.preventDefault(); | |
| e.dataTransfer.dropEffect = 'move'; | |
| pill.classList.add('pc-over'); | |
| }); | |
| pill.addEventListener('dragleave', function() { | |
| pill.classList.remove('pc-over'); | |
| }); | |
| pill.addEventListener('drop', function(e) { | |
| e.preventDefault(); | |
| pill.classList.remove('pc-over'); | |
| const from = parseInt(e.dataTransfer.getData('text/plain'), 10); | |
| const to = slot; | |
| if (from === to || isNaN(from)) return; | |
| const moved = pcOrder.splice(from, 1)[0]; | |
| pcOrder.splice(to, 0, moved); | |
| try { | |
| localStorage.setItem(STORE_KEY_PARCOORDS, JSON.stringify(pcOrder)); | |
| } catch(err) {} | |
| if (pcChart) { | |
| pcChart.setOption(buildParcoordsOption(records), { notMerge: true }); | |
| renderHandles(records); | |
| } | |
| }); | |
| container.appendChild(pill); | |
| }); | |
| } | |
| function resetPillars(e) { | |
| if (e) e.preventDefault(); | |
| pcOrder = [0, 1, 2, 3, 4, 5, 6, 7]; | |
| try { | |
| localStorage.removeItem(STORE_KEY_PARCOORDS); | |
| } catch(err) {} | |
| const records = getPillarData(); | |
| if (pcChart) { | |
| pcChart.setOption(buildParcoordsOption(records), { notMerge: true }); | |
| renderHandles(records); | |
| } | |
| } | |
| function renderSweepLeaderboard(records) { | |
| const tbody = document.getElementById('sweep-leaderboard-body'); | |
| const countEl = document.getElementById('leaderboard-count'); | |
| if (!tbody) return; | |
| if (countEl) countEl.textContent = `${records.length} runs ranked`; | |
| tbody.innerHTML = records.map(r => { | |
| let rankBadge = `<span style="color:var(--text-muted); font-weight:700; font-size:0.85rem;">#${r.rank}</span>`; | |
| if (r.rank === 1) rankBadge = `<span class="rank-badge-gold">#1 🥇 Champion</span>`; | |
| else if (r.rank === 2) rankBadge = `<span class="rank-badge-silver">#2 🥈 Runner-Up</span>`; | |
| else if (r.rank === 3) rankBadge = `<span class="rank-badge-bronze">#3 🥉 Third</span>`; | |
| const orderBadge = r.ordering === 'Cosine' | |
| ? `<span class="badge badge-success" style="font-size:0.75rem;">Cosine Sorted</span>` | |
| : `<span class="badge badge-info" style="font-size:0.75rem;">Random Perm</span>`; | |
| return ` | |
| <tr style="border-bottom: 1px solid rgba(48, 54, 61, 0.5); transition: background 0.15s ease;" onmouseover="this.style.background='rgba(255,255,255,0.03)'" onmouseout="this.style.background='transparent'"> | |
| <td style="padding: 10px;">${rankBadge}</td> | |
| <td style="padding: 10px;"> | |
| <div style="font-weight: 600; color: #f0f6fc;">${r.name}</div> | |
| <div style="font-size: 0.75rem; color: var(--text-muted); font-family: monospace;">${r.id}</div> | |
| </td> | |
| <td style="padding: 10px; font-weight: 600; color: #c9d1d9;">${r.layers}L <span style="font-size:0.75rem; color:var(--text-muted);">(${r.steps}s)</span></td> | |
| <td style="padding: 10px; font-family: monospace; color: #79c0ff;">${r.lr_label}</td> | |
| <td style="padding: 10px;">${orderBadge}</td> | |
| <td style="padding: 10px; font-weight: 700; color: #58a6ff;">${r.val_loss.toFixed(4)}</td> | |
| <td style="padding: 10px; font-weight: 600; color: #3fb950;">${r.align.toFixed(3)}</td> | |
| <td style="padding: 10px; color: #bc8cff;">${r.diversity.toFixed(3)}</td> | |
| <td style="padding: 10px; color: #f0883e;">${r.latency.toFixed(1)} ms</td> | |
| <td style="padding: 10px; text-align: center;"> | |
| <button class="btn-action" style="padding: 4px 10px; font-size: 0.75rem;" onclick="jumpToDrill('${r.id}')">Inspect Decodings →</button> | |
| </td> | |
| </tr> | |
| `; | |
| }).join(''); | |
| } | |
| function renderSweepRankChart() { | |
| const records = getPillarData(); | |
| renderSweepLeaderboard(records); | |
| const chartDom = document.getElementById('parcoords-echart'); | |
| if (!chartDom) return; | |
| if (!pcChart) { | |
| pcChart = echarts.init(chartDom, null, { renderer: 'canvas' }); | |
| pcChart.on('click', { seriesIndex: 0 }, function(params) { | |
| const runId = params.value[9]; | |
| if (runId) { | |
| jumpToDrill(runId); | |
| } | |
| }); | |
| pcChart.on('axisAreaSelected', function(params) { | |
| const selCountEl = document.getElementById('pc-selected-count'); | |
| if (selCountEl) { | |
| const intervals = params.intervals; | |
| let active = 0; | |
| records.forEach(r => { | |
| const row = [r.rank, r.layers, r.lr, r.ordering, r.latency, r.diversity, r.align, r.val_loss]; | |
| let match = true; | |
| intervals.forEach((iv, dimIdx) => { | |
| if (!iv || iv.length === 0) return; | |
| const v = row[dimIdx]; | |
| if (typeof v === 'number') { | |
| const inAny = iv.some(range => v >= range[0] && v <= range[1]); | |
| if (!inAny) match = false; | |
| } | |
| }); | |
| if (match) active++; | |
| }); | |
| selCountEl.textContent = `${active} / ${records.length}`; | |
| } | |
| }); | |
| window.addEventListener('resize', function() { | |
| if (pcChart) { | |
| pcChart.resize(); | |
| const curRecords = getPillarData(); | |
| renderHandles(curRecords); | |
| } | |
| }); | |
| } | |
| if (pcChart) pcChart.resize(); | |
| const opt = buildParcoordsOption(records); | |
| pcChart.setOption(opt, { notMerge: true }); | |
| renderHandles(records); | |
| const selCountEl = document.getElementById('pc-selected-count'); | |
| if (selCountEl) selCountEl.textContent = `${records.length} / ${records.length}`; | |
| const metricSelect = document.getElementById('sweep-rank-metric'); | |
| const colorLabel = document.getElementById('pc-color-label'); | |
| if (metricSelect && colorLabel) { | |
| const text = metricSelect.options[metricSelect.selectedIndex]?.text || 'Rank'; | |
| colorLabel.textContent = text.replace('Rank by: ', '') + ' (#1 Best → Low)'; | |
| } | |
| } | |
| function scrollToRankChart() { | |
| switchTab('overview'); | |
| const el = document.getElementById('sweep-rank-section'); | |
| if (el) { | |
| el.scrollIntoView({ behavior: 'smooth', block: 'start' }); | |
| } | |
| } | |
| function renderRunsTable() { | |
| const tbody = document.getElementById('runs-table-body'); | |
| if (!tbody) return; | |
| const q = (document.getElementById('search-runs')?.value || '').toLowerCase(); | |
| let filtered = allRuns.filter(r => { | |
| if (currentFilter !== 'all' && r.status !== currentFilter) return false; | |
| if (q) { | |
| const match = r.name.toLowerCase().includes(q) || | |
| (r.experiment_name || '').toLowerCase().includes(q) || | |
| JSON.stringify(r.config).toLowerCase().includes(q); | |
| if (!match) return false; | |
| } | |
| return true; | |
| }); | |
| filtered.sort((a, b) => { | |
| let vA = getRunSortVal(a, currentSortKey); | |
| let vB = getRunSortVal(b, currentSortKey); | |
| if (vA === null) return 1; | |
| if (vB === null) return -1; | |
| if (typeof vA === 'string') return currentSortAsc ? vA.localeCompare(vB) : vB.localeCompare(vA); | |
| return currentSortAsc ? vA - vB : vB - vA; | |
| }); | |
| tbody.innerHTML = filtered.map(r => { | |
| const s = r.summary || {}; | |
| const c = r.config || {}; | |
| const lat = s.sampling_latency_ms || (r.latency_us ? r.latency_us / 1000.0 : null); | |
| const statusBadge = r.status === 'completed' | |
| ? '<span class="badge badge-success">Completed</span>' | |
| : (r.status === 'running' ? '<span class="badge badge-running">Running</span>' : '<span class="badge badge-failed">Failed</span>'); | |
| return ` | |
| <tr> | |
| <td style="font-weight:600; color:#f0f6fc;">${r.name}</td> | |
| <td style="color:var(--text-muted);">${r.experiment_name || 'Default'}</td> | |
| <td>${statusBadge}</td> | |
| <td>${c.layers || '-'}</td> | |
| <td>${c.target_ordering || 'random_perm'}</td> | |
| <td style="font-weight:600; color:#79c0ff;">${s.best_val_loss ? s.best_val_loss.toFixed(4) : '-'}</td> | |
| <td>${s.prompt_alignment ? s.prompt_alignment.toFixed(3) : '-'}</td> | |
| <td>${s.pairwise_diversity ? s.pairwise_diversity.toFixed(3) : '-'}</td> | |
| <td>${lat ? lat.toFixed(1) : '-'}</td> | |
| <td style="color:var(--text-muted); font-size:0.8rem;">${r.started_at ? r.started_at.split(' ')[0] : '-'}</td> | |
| <td style="text-align:center;"> | |
| <button class="btn-action" onclick="jumpToDrill('${r.id}')">Inspect</button> | |
| </td> | |
| </tr> | |
| `; | |
| }).join(''); | |
| } | |
| function getRunSortVal(r, key) { | |
| const s = r.summary || {}; | |
| const c = r.config || {}; | |
| switch(key) { | |
| case 'name': return r.name; | |
| case 'experiment_name': return r.experiment_name; | |
| case 'status': return r.status; | |
| case 'layers': return c.layers || 0; | |
| case 'ordering': return c.target_ordering || ''; | |
| case 'best_val_loss': return s.best_val_loss !== undefined ? s.best_val_loss : null; | |
| case 'prompt_align': return s.prompt_alignment !== undefined ? s.prompt_alignment : null; | |
| case 'diversity': return s.pairwise_diversity !== undefined ? s.pairwise_diversity : null; | |
| case 'latency': return s.sampling_latency_ms || (r.latency_us ? r.latency_us / 1000.0 : null); | |
| case 'started_at': return r.started_at; | |
| default: return r[key]; | |
| } | |
| } | |
| function setSort(key) { | |
| if (currentSortKey === key) { | |
| currentSortAsc = !currentSortAsc; | |
| } else { | |
| currentSortKey = key; | |
| currentSortAsc = true; | |
| } | |
| renderRunsTable(); | |
| } | |
| function filterTask(f) { | |
| currentFilter = f; | |
| document.querySelectorAll('.filter-pills .pill').forEach(p => p.classList.remove('active')); | |
| const activePill = Array.from(document.querySelectorAll('.filter-pills .pill')).find(p => p.getAttribute('onclick')?.includes(f)); | |
| if (activePill) activePill.classList.add('active'); | |
| renderRunsTable(); | |
| } | |
| function populateRunSelect() { | |
| const select = document.getElementById('run-select'); | |
| if (!select) return; | |
| select.innerHTML = allRuns.map(r => `<option value="${r.id}">${r.name}</option>`).join(''); | |
| if (selectedRunId) select.value = selectedRunId; | |
| } | |
| function jumpToDrill(runId, fromSelect = false) { | |
| selectedRunId = runId; | |
| if (!fromSelect) { | |
| const select = document.getElementById('run-select'); | |
| if (select) select.value = runId; | |
| switchTab('drilldown'); | |
| } else { | |
| loadRunDetail(runId); | |
| } | |
| } | |
| async function loadRunDetail(runId) { | |
| try { | |
| const res = await fetch(`/api/runs/${runId}`); | |
| const r = await res.json(); | |
| document.getElementById('drill-name').textContent = r.name; | |
| document.getElementById('drill-exp').textContent = `Experiment: ${r.experiment_name || 'Default'}`; | |
| document.getElementById('drill-status').textContent = r.status.toUpperCase(); | |
| document.getElementById('drill-config').textContent = JSON.stringify(r.config, null, 2); | |
| // Hardware info | |
| const bInfo = document.getElementById('drill-benchmark-info'); | |
| if (r.benchmark) { | |
| bInfo.innerHTML = ` | |
| <div>Device: <b>${r.benchmark.device_name || 'RTX 4090'}</b></div> | |
| <div>Latency: <b>${(r.benchmark.latency_us / 1000.0).toFixed(1)} ms</b></div> | |
| <div>Throughput: <b>${(r.benchmark.throughput || 0).toFixed(1)} sets/sec</b></div> | |
| <div>Notes: <i>${r.benchmark.notes || 'N/A'}</i></div> | |
| `; | |
| } else { | |
| bInfo.textContent = 'No hardware benchmark recorded for this run.'; | |
| } | |
| // Render Dynamic Metric Charts | |
| renderDrillCharts(r.metrics || {}); | |
| // Render WandB Style Samples | |
| renderDrillSamples(r.samples || []); | |
| } catch (err) { | |
| console.error('Failed to load run detail:', err); | |
| } | |
| } | |
| function renderDrillCharts(metrics) { | |
| const container = document.getElementById('drill-charts-container'); | |
| if (!container) return; | |
| const keys = Object.keys(metrics); | |
| if (keys.length === 0) { | |
| container.innerHTML = '<div class="chart-card" style="grid-column: 1 / -1; color: var(--text-muted); text-align: center; padding: 2rem;">No metric trajectories recorded for this run.</div>'; | |
| return; | |
| } | |
| container.innerHTML = keys.map(k => ` | |
| <div class="chart-card"> | |
| <div class="chart-header"> | |
| <div class="chart-title">${k}</div> | |
| </div> | |
| <div id="chart-drill-${k}" class="chart-box"></div> | |
| </div> | |
| `).join(''); | |
| keys.forEach(k => { | |
| const m = metrics[k]; | |
| const el = document.getElementById(`chart-drill-${k}`); | |
| if (el) { | |
| const isEpoch = m.epochs && m.epochs.length > 0 && m.epochs[0] !== null; | |
| const rawX = isEpoch ? m.epochs : m.steps; | |
| const xVals = rawX.map(Number); | |
| const yVals = m.values.map(Number); | |
| Plotly.newPlot(el, [{ | |
| x: xVals, | |
| y: yVals, | |
| mode: 'lines+markers', | |
| line: { color: '#58a6ff', width: 2 }, | |
| marker: { size: 5, color: '#79c0ff' }, | |
| hovertemplate: `${isEpoch ? 'Epoch' : 'Step'} %{x}<br>${k}: %{y:.5f}<extra></extra>` | |
| }], { | |
| ...darkLayout, | |
| margin: { l: 70, r: 25, t: 30, b: 40 }, | |
| xaxis: { | |
| ...darkLayout.xaxis, | |
| type: 'linear', | |
| title: isEpoch ? 'Epoch' : 'Step', | |
| dtick: isEpoch ? 1 : undefined | |
| }, | |
| yaxis: { | |
| ...darkLayout.yaxis, | |
| type: 'linear', | |
| title: k, | |
| autorange: true, | |
| tickformat: k === 'lr' ? '.2e' : '.4f' | |
| } | |
| }, { responsive: true, displayModeBar: false }); | |
| } | |
| }); | |
| } | |
| function renderDrillSamples(samples) { | |
| const grid = document.getElementById('drill-samples-grid'); | |
| if (!grid) return; | |
| if (samples.length === 0) { | |
| grid.innerHTML = '<div class="sample-card" style="grid-column: 1 / -1; color: var(--text-muted); text-align: center;">No fan-out query outputs logged for this run.</div>'; | |
| return; | |
| } | |
| grid.innerHTML = samples.map((s, idx) => { | |
| const inp = s.input || {}; | |
| const out = s.output || {}; | |
| const meta = s.metadata || {}; | |
| const subqueries = out.subqueries || []; | |
| return ` | |
| <div class="sample-card"> | |
| <div class="sample-head"> | |
| <span><b>Sample #${idx + 1}</b> (Step ${s.step})</span> | |
| <span>${s.timestamp || ''}</span> | |
| </div> | |
| <div style="font-size: 0.92rem; margin-bottom: 6px;"> | |
| Input Broad Query: <b style="color: #79c0ff;">"${inp.query || 'N/A'}"</b> | |
| </div> | |
| ${out.mean_prompt_align !== undefined ? ` | |
| <div style="font-size: 0.8rem; color: var(--text-muted); margin-bottom: 8px;"> | |
| Alignment: <b>${out.mean_prompt_align.toFixed(3)}</b> | Diversity: <b>${out.pairwise_diversity ? out.pairwise_diversity.toFixed(3) : '-'}</b> | |
| </div> | |
| ` : ''} | |
| <div class="subquery-list"> | |
| ${subqueries.map((sq, sIdx) => ` | |
| <div class="subquery-item"> | |
| <span><span class="subquery-slot">#${sIdx + 1}</span>"${sq}"</span> | |
| </div> | |
| `).join('')} | |
| </div> | |
| ${meta.notes || meta.domain_coherence ? ` | |
| <div style="margin-top: 8px; font-size: 0.78rem; color: #d29922;"> | |
| ${meta.notes || meta.domain_coherence} | |
| </div> | |
| ` : ''} | |
| </div> | |
| `; | |
| }).join(''); | |
| } | |
| // Live Fanout Playground Logic | |
| function setPlaygroundQuery(q) { | |
| document.getElementById('playground-query-input').value = q; | |
| executeLiveFanout(); | |
| } | |
| async function executeLiveFanout() { | |
| const input = document.getElementById('playground-query-input'); | |
| const query = input.value.trim(); | |
| if (!query) return; | |
| const btn = document.getElementById('playground-submit-btn'); | |
| btn.disabled = true; | |
| btn.innerText = '⚡ Synthesizing Fanout...'; | |
| try { | |
| const res = await fetch('/api/playground/fanout', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ query: query }) | |
| }); | |
| const data = await res.json(); | |
| if (!res.ok) throw new Error(data.detail || 'Failed to generate fanout'); | |
| const banner = document.getElementById('playground-stats-banner'); | |
| banner.style.display = 'flex'; | |
| document.getElementById('pg-fanout-lat').innerText = `${data.latency_ms} ms (${(data.latency_ms * 1000).toFixed(0)} µs)`; | |
| document.getElementById('pg-embed-lat').innerText = `${data.embed_latency_ms} ms`; | |
| document.getElementById('pg-total-lat').innerText = `${data.total_latency_ms} ms`; | |
| document.getElementById('pg-align').innerText = `${data.mean_alignment.toFixed(3)} Cosine`; | |
| document.getElementById('pg-div').innerText = `${data.pairwise_diversity.toFixed(3)}`; | |
| const grid = document.getElementById('playground-slots-grid'); | |
| grid.innerHTML = data.slots.map(s => ` | |
| <div style="background: rgba(255,255,255,0.02); border: 1px solid var(--card-border); border-left: 3px solid var(--accent-green); border-radius: 6px; padding: 12px; display: flex; flex-direction: column; justify-content: space-between;"> | |
| <div> | |
| <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px;"> | |
| <span style="font-size: 0.75rem; font-weight: 700; color: var(--accent-green); text-transform: uppercase;">Slot #${s.slot}</span> | |
| <span style="font-size: 0.75rem; color: var(--text-muted);">Align: <b>${s.alignment}</b></span> | |
| </div> | |
| <div style="font-size: 0.95rem; font-weight: 700; color: #f0f6fc; margin-bottom: 4px; word-break: break-word;"> | |
| ${s.subquery ? s.subquery : s.category} | |
| </div> | |
| <div style="font-size: 0.78rem; color: var(--accent-blue); margin-bottom: 8px; font-weight: 500;"> | |
| 🏷️ ${s.category} | |
| </div> | |
| </div> | |
| <div style="display: flex; justify-content: space-between; font-size: 0.72rem; color: var(--text-muted); border-top: 1px solid rgba(255,255,255,0.05); padding-top: 6px;"> | |
| <span>Subquery Sim: <b style="color: var(--accent-green);">${s.subquery_similarity || s.similarity}</b></span> | |
| <span>Tax Sim: <b style="color: var(--accent-blue);">${s.similarity}</b></span> | |
| </div> | |
| </div> | |
| `).join(''); | |
| } catch (err) { | |
| alert('Error generating fanout: ' + err.message); | |
| } finally { | |
| btn.disabled = false; | |
| btn.innerText = '⚡ Generate Fanout (169 µs)'; | |
| } | |
| } | |
| // Auto-load on page start | |
| window.addEventListener('DOMContentLoaded', loadAllData); | |
| </script> | |
| </body> | |
| </html> | |