AgentStateGraph / app /src /content /embeds /baseline-heatmap.html
seonglae's picture
Automata from Agent Traces — interactive article
59027a2
Raw
History Blame Contribute Delete
8.95 kB
<!-- Baseline Heatmap: Interactive comparison table of methods x datasets -->
<div class="baseline-heatmap"></div>
<style>
.baseline-heatmap { position: relative; width: 100%; }
.baseline-heatmap .controls { display: flex; gap: 12px; margin-bottom: 12px; align-items: center; flex-wrap: wrap; }
.baseline-heatmap .seg-control {
display: inline-flex; background: var(--surface-bg);
border: 1px solid var(--border-color); border-radius: 8px;
padding: 3px; gap: 2px;
}
.baseline-heatmap .seg-control button {
padding: 5px 14px; border-radius: 6px; border: none;
background: transparent; font-size: 12px; font-weight: 500;
color: var(--text-color); cursor: pointer; transition: all 0.15s ease;
opacity: 0.6;
}
.baseline-heatmap .seg-control button:hover { opacity: 0.8; }
.baseline-heatmap .seg-control button.active {
background: var(--text-color); color: var(--page-bg);
opacity: 1; font-weight: 600;
}
.baseline-heatmap .table-wrap {
overflow-x: auto; border: 1px solid var(--border-color); border-radius: 10px;
}
.baseline-heatmap table {
border-collapse: separate; border-spacing: 0; width: 100%;
font-size: 12px; line-height: 1.4; font-variant-numeric: tabular-nums;
}
.baseline-heatmap thead th {
background: var(--surface-bg); color: var(--text-color); font-weight: 600;
padding: 10px 10px; text-align: center; white-space: nowrap;
position: sticky; top: 0; z-index: 2;
border-bottom: 1px solid var(--border-color);
}
.baseline-heatmap thead th:first-child {
text-align: left; position: sticky; left: 0; z-index: 3;
border-right: 1px solid var(--border-color);
}
.baseline-heatmap tbody td {
padding: 8px 10px; text-align: center;
border-bottom: 1px solid color-mix(in srgb, var(--border-color) 40%, transparent);
transition: background 0.15s ease, opacity 0.25s ease;
}
.baseline-heatmap tbody td:first-child {
text-align: left; font-weight: 600; background: var(--surface-bg);
position: sticky; left: 0; z-index: 1; white-space: nowrap;
border-right: 1px solid var(--border-color);
}
.baseline-heatmap tbody tr:last-child td { border-bottom: none; }
.baseline-heatmap tbody tr:hover td { background: color-mix(in srgb, var(--text-color) 6%, transparent); }
.baseline-heatmap tbody tr:hover td:first-child { background: color-mix(in srgb, var(--text-color) 8%, var(--surface-bg)); }
.baseline-heatmap td.best { font-weight: 700; }
.baseline-heatmap td.col-hover { background: color-mix(in srgb, var(--text-color) 4%, transparent) !important; }
.baseline-heatmap td.fade-in { animation: bh-fade 0.3s ease forwards; }
@keyframes bh-fade { from { opacity: 0; transform: translateY(2px); } to { opacity: 1; transform: translateY(0); } }
</style>
<script>
(() => {
const container = document.querySelector('.baseline-heatmap:not([data-mounted])');
if (!container) return;
container.dataset.mounted = 'true';
const datasets = ['SWE-smith','SWE-agent','WebArena','AgentNet','ATBench','OSWorld','tau2-air','tau2-ret','tau2-tel','W&W','M2W','GUI-Ody'];
const statesData = {
'Ours': [10, 25, 25, 25, 15, 27, 18, 19, 43, 9, 8, 7],
'RPNI': [11631, 59510, 382, 62495, 899, 38232, 6506, 14249, 63897, 971, 476, 21255],
'Alergia': [10, 35, 149, 45, 15, 31, 23, 25, 75, 12, 8, 24],
'HMM': [10, 25, 25, 25, 15, 27, 18, 19, 43, 9, 8, 7],
'EDSM': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
};
const fitnessData = {
'Ours': [1.000, 0.999, 1.000, 1.000, 1.000, 0.997, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000],
'RPNI': [0.761, 0.646, 1.000, 0.742, 0.984, 0.706, 0.844, 0.837, 0.491, 0.984, 0.970, 0.929],
'Alergia': [1.000, 0.999, 1.000, 1.000, 1.000, 0.999, 0.999, 1.000, 0.999, 1.000, 1.000, 1.000],
'HMM': [1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000],
'EDSM': [1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000, 1.000],
};
const methods = Object.keys(statesData);
let metric = 'states';
// --- Controls ---
const controls = document.createElement('div');
controls.className = 'controls';
const seg = document.createElement('div');
seg.className = 'seg-control';
const btnStates = document.createElement('button');
btnStates.textContent = 'State Count'; btnStates.className = 'active';
const btnFitness = document.createElement('button');
btnFitness.textContent = 'Fitness';
seg.appendChild(btnStates); seg.appendChild(btnFitness);
controls.appendChild(seg);
container.prepend(controls);
btnStates.addEventListener('click', () => { metric = 'states'; btnStates.className = 'active'; btnFitness.className = ''; renderTable(); });
btnFitness.addEventListener('click', () => { metric = 'fitness'; btnFitness.className = 'active'; btnStates.className = ''; renderTable(); });
// --- Table wrapper ---
const wrap = document.createElement('div');
wrap.className = 'table-wrap';
container.appendChild(wrap);
// --- Best value per column (returns the value, so EVERY tied cell can be bolded) ---
function findBestVal(colIndex, data) {
let bestVal = null;
methods.forEach((m) => {
const v = data[m][colIndex];
if (v === '-') return;
if (metric === 'states') {
// Best = lowest non-degenerate (>1)
if (v > 1 && (bestVal === null || v < bestVal)) bestVal = v;
} else {
// Best = highest
if (bestVal === null || v > bestVal) bestVal = v;
}
});
return bestVal;
}
// A cell is "best" if it equals the column's best value (ties all qualify).
function isBest(val, bestVal) {
if (bestVal === null || val === '-') return false;
if (metric === 'states') return typeof val === 'number' && val > 1 && val === bestVal;
return val === bestVal;
}
// --- Color logic ---
function cellBg(val, m) {
if (val === '-') return 'transparent';
if (metric === 'states') {
if (m === 'EDSM') return 'rgba(200,200,200,0.12)';
const logVal = Math.log10(Math.max(1, val));
const t = Math.min(1, logVal / 5);
// low states = teal tint, high = rust tint
if (t < 0.3) return `rgba(42,157,143,${0.08 + t * 0.15})`;
return `rgba(61, 90, 128,${0.06 + (t - 0.3) * 0.2})`;
} else {
const t = Math.max(0, (val - 0.5) / 0.5);
return `rgba(42,157,143,${t * 0.35})`;
}
}
// --- Column hover ---
let hoveredCol = -1;
function updateColHover(colIdx) {
if (hoveredCol === colIdx) return;
hoveredCol = colIdx;
const cells = wrap.querySelectorAll('td, th');
cells.forEach(c => c.classList.remove('col-hover'));
if (colIdx < 0) return;
const rows = wrap.querySelectorAll('tr');
rows.forEach(row => {
const cell = row.children[colIdx];
if (cell) cell.classList.add('col-hover');
});
}
function renderTable() {
wrap.innerHTML = '';
const currentData = metric === 'states' ? statesData : fitnessData;
// Precompute best per column
const bestPerCol = datasets.map((_, ci) => findBestVal(ci, currentData));
const table = document.createElement('table');
// --- Header ---
const thead = document.createElement('thead');
const hr = document.createElement('tr');
const th0 = document.createElement('th');
th0.textContent = 'Method';
hr.appendChild(th0);
datasets.forEach((d, ci) => {
const th = document.createElement('th');
th.textContent = d;
th.addEventListener('mouseenter', () => updateColHover(ci + 1));
th.addEventListener('mouseleave', () => updateColHover(-1));
hr.appendChild(th);
});
thead.appendChild(hr);
table.appendChild(thead);
// --- Body ---
const tbody = document.createElement('tbody');
methods.forEach((method, mi) => {
const tr = document.createElement('tr');
const td0 = document.createElement('td');
td0.textContent = method;
tr.appendChild(td0);
currentData[method].forEach((val, ci) => {
const td = document.createElement('td');
td.style.background = cellBg(val, method);
td.className = 'fade-in';
td.style.animationDelay = `${mi * 20 + ci * 10}ms`;
if (val === '-') {
td.textContent = '-';
td.style.opacity = '0.35';
} else if (metric === 'states') {
td.textContent = typeof val === 'number' ? val.toLocaleString() : val;
} else {
td.textContent = typeof val === 'number' ? val.toFixed(3) : val;
}
if (isBest(val, bestPerCol[ci])) td.classList.add('best');
td.addEventListener('mouseenter', () => updateColHover(ci + 1));
td.addEventListener('mouseleave', () => updateColHover(-1));
tr.appendChild(td);
});
tbody.appendChild(tr);
});
table.appendChild(tbody);
wrap.appendChild(table);
}
renderTable();
})();
</script>