Spaces:
Running
Running
EvoTool interactive replay (static)
Browse files- README.md +16 -4
- index.html +396 -18
- static/replay_data.js +0 -0
README.md
CHANGED
|
@@ -1,10 +1,22 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
colorTo: indigo
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
|
|
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: EvoTool Interactive Replay
|
| 3 |
+
emoji: 🔁
|
| 4 |
+
colorFrom: purple
|
| 5 |
colorTo: indigo
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
| 8 |
+
license: mit
|
| 9 |
+
short_description: Generation-by-generation replay of EvoTool's evolution loop
|
| 10 |
---
|
| 11 |
|
| 12 |
+
# EvoTool — Interactive Replay
|
| 13 |
+
|
| 14 |
+
Generation-by-generation replay of the EvoTool self-evolving loop (blame → targeted
|
| 15 |
+
mutation → diversity-aware selection) on a reference run: every one of the 90
|
| 16 |
+
generations with the Blamer's diagnosis, the prompt diff, and the population state.
|
| 17 |
+
|
| 18 |
+
- Paper: **EvoTool: Self-Evolving Tool-Use Policy Optimization in LLM Agents via
|
| 19 |
+
Blame-Aware Mutation and Diversity-Aware Selection** (ACL 2026) —
|
| 20 |
+
https://aclanthology.org/2026.acl-long.2016/ · arXiv: https://arxiv.org/abs/2603.04900
|
| 21 |
+
- Code: https://github.com/SYang2000/ACL_2026_EvoTool
|
| 22 |
+
- Project page: https://syang2000.github.io/ACL_2026_EvoTool/
|
index.html
CHANGED
|
@@ -1,19 +1,397 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
</html>
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
| 6 |
+
<title>EvoTool — Generation-by-Generation Replay</title>
|
| 7 |
+
<style>
|
| 8 |
+
:root {
|
| 9 |
+
--ink: #1c2430;
|
| 10 |
+
--muted: #5b6a7c;
|
| 11 |
+
--line: #dde4ec;
|
| 12 |
+
--bg: #f6f8fa;
|
| 13 |
+
--card: #ffffff;
|
| 14 |
+
--accent: #1f5fbf;
|
| 15 |
+
--planner: #1f5fbf;
|
| 16 |
+
--selector: #7b3fbf;
|
| 17 |
+
--caller: #c96a11;
|
| 18 |
+
--synthesizer: #1e8a4c;
|
| 19 |
+
--ok: #1e8a4c;
|
| 20 |
+
--bad: #b3402f;
|
| 21 |
+
}
|
| 22 |
+
* { box-sizing: border-box; }
|
| 23 |
+
body {
|
| 24 |
+
margin: 0;
|
| 25 |
+
background: var(--bg);
|
| 26 |
+
color: var(--ink);
|
| 27 |
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
|
| 28 |
+
font-size: 15px;
|
| 29 |
+
line-height: 1.5;
|
| 30 |
+
}
|
| 31 |
+
a { color: var(--accent); text-decoration: none; }
|
| 32 |
+
a:hover { text-decoration: underline; }
|
| 33 |
+
.wrap { max-width: 980px; margin: 0 auto; padding: 20px 18px 60px; }
|
| 34 |
+
header h1 { font-size: 1.45rem; margin: 8px 0 4px; }
|
| 35 |
+
header .sub { color: var(--muted); font-size: .92rem; }
|
| 36 |
+
.navlinks { font-size: .9rem; margin-top: 6px; }
|
| 37 |
+
.navlinks a + a { margin-left: 14px; }
|
| 38 |
+
.banner {
|
| 39 |
+
margin: 14px 0 18px;
|
| 40 |
+
padding: 10px 14px;
|
| 41 |
+
background: #fff7e8;
|
| 42 |
+
border: 1px solid #ecd9ac;
|
| 43 |
+
border-radius: 8px;
|
| 44 |
+
font-size: .88rem;
|
| 45 |
+
color: #6a5320;
|
| 46 |
+
}
|
| 47 |
+
.card {
|
| 48 |
+
background: var(--card);
|
| 49 |
+
border: 1px solid var(--line);
|
| 50 |
+
border-radius: 10px;
|
| 51 |
+
padding: 16px 18px;
|
| 52 |
+
margin-bottom: 16px;
|
| 53 |
+
}
|
| 54 |
+
/* controls */
|
| 55 |
+
.controls { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
|
| 56 |
+
.controls button {
|
| 57 |
+
font: inherit;
|
| 58 |
+
padding: 5px 14px;
|
| 59 |
+
border: 1px solid var(--line);
|
| 60 |
+
border-radius: 7px;
|
| 61 |
+
background: #fff;
|
| 62 |
+
color: var(--ink);
|
| 63 |
+
cursor: pointer;
|
| 64 |
+
}
|
| 65 |
+
.controls button:hover { border-color: var(--accent); color: var(--accent); }
|
| 66 |
+
.controls input[type=range] { flex: 1; min-width: 180px; accent-color: var(--accent); }
|
| 67 |
+
.controls .genlabel { font-variant-numeric: tabular-nums; font-weight: 600; min-width: 120px; }
|
| 68 |
+
.hint { color: var(--muted); font-size: .8rem; margin-top: 6px; }
|
| 69 |
+
/* generation panel */
|
| 70 |
+
.genhead { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
|
| 71 |
+
.genhead .gid { font-weight: 700; font-size: 1.05rem; }
|
| 72 |
+
.chip {
|
| 73 |
+
display: inline-block;
|
| 74 |
+
padding: 1px 10px;
|
| 75 |
+
border-radius: 999px;
|
| 76 |
+
font-size: .78rem;
|
| 77 |
+
font-weight: 600;
|
| 78 |
+
color: #fff;
|
| 79 |
+
}
|
| 80 |
+
.chip.planner { background: var(--planner); }
|
| 81 |
+
.chip.selector { background: var(--selector); }
|
| 82 |
+
.chip.caller { background: var(--caller); }
|
| 83 |
+
.chip.synthesizer { background: var(--synthesizer); }
|
| 84 |
+
.badge {
|
| 85 |
+
display: inline-block;
|
| 86 |
+
padding: 1px 10px;
|
| 87 |
+
border-radius: 6px;
|
| 88 |
+
font-size: .78rem;
|
| 89 |
+
font-weight: 700;
|
| 90 |
+
letter-spacing: .03em;
|
| 91 |
+
}
|
| 92 |
+
.badge.accepted { background: #e2f3e8; color: var(--ok); border: 1px solid #bcdfc9; }
|
| 93 |
+
.badge.rejected { background: #f7e8e5; color: var(--bad); border: 1px solid #e6c5be; }
|
| 94 |
+
.stats { display: flex; gap: 22px; flex-wrap: wrap; font-size: .9rem; margin: 6px 0 10px; }
|
| 95 |
+
.stats .k { color: var(--muted); }
|
| 96 |
+
.stats .v { font-weight: 600; font-variant-numeric: tabular-nums; }
|
| 97 |
+
.up { color: var(--ok); } .down { color: var(--bad); }
|
| 98 |
+
.diag {
|
| 99 |
+
border-left: 3px solid var(--accent);
|
| 100 |
+
background: #f2f6fc;
|
| 101 |
+
margin: 10px 0;
|
| 102 |
+
padding: 8px 12px;
|
| 103 |
+
font-size: .9rem;
|
| 104 |
+
border-radius: 0 6px 6px 0;
|
| 105 |
+
}
|
| 106 |
+
.diag .who { font-weight: 700; font-size: .8rem; color: var(--accent); display: block; margin-bottom: 2px; }
|
| 107 |
+
pre.diff {
|
| 108 |
+
background: #0f1720;
|
| 109 |
+
color: #cfd8e3;
|
| 110 |
+
border-radius: 8px;
|
| 111 |
+
padding: 12px 14px;
|
| 112 |
+
overflow-x: auto;
|
| 113 |
+
font-size: .8rem;
|
| 114 |
+
line-height: 1.45;
|
| 115 |
+
white-space: pre-wrap;
|
| 116 |
+
word-break: break-word;
|
| 117 |
+
}
|
| 118 |
+
pre.diff .add { color: #7fd08a; }
|
| 119 |
+
pre.diff .del { color: #f08f80; }
|
| 120 |
+
pre.diff .hunk { color: #8fa6c0; font-weight: 600; }
|
| 121 |
+
pre.diff .filehdr { color: #8fa6c0; }
|
| 122 |
+
canvas { width: 100%; height: 190px; display: block; }
|
| 123 |
+
.charthead { font-size: .85rem; color: var(--muted); margin-bottom: 6px; }
|
| 124 |
+
.infonote {
|
| 125 |
+
margin: -4px 0 16px;
|
| 126 |
+
padding: 6px 14px;
|
| 127 |
+
border-left: 3px solid var(--line);
|
| 128 |
+
color: var(--muted);
|
| 129 |
+
font-size: .84rem;
|
| 130 |
+
}
|
| 131 |
+
#error {
|
| 132 |
+
display: none;
|
| 133 |
+
background: #f7e8e5;
|
| 134 |
+
border: 1px solid #e6c5be;
|
| 135 |
+
color: var(--bad);
|
| 136 |
+
padding: 14px 16px;
|
| 137 |
+
border-radius: 8px;
|
| 138 |
+
margin-top: 14px;
|
| 139 |
+
}
|
| 140 |
+
footer { color: var(--muted); font-size: .8rem; margin-top: 26px; }
|
| 141 |
+
</style>
|
| 142 |
+
</head>
|
| 143 |
+
<body>
|
| 144 |
+
<div class="wrap">
|
| 145 |
+
<header>
|
| 146 |
+
<h1>EvoTool — Generation-by-Generation Replay</h1>
|
| 147 |
+
<div class="sub">Every mutation the self-evolving loop proposed on BFCL: what the Blamer diagnosed,
|
| 148 |
+
what the Mutator changed, and whether the child beat its parent.</div>
|
| 149 |
+
<div class="navlinks">
|
| 150 |
+
<a href="index.html">← Project page</a>
|
| 151 |
+
<a href="https://github.com/SYang2000/ACL_2026_EvoTool" target="_blank" rel="noopener">GitHub repo</a>
|
| 152 |
+
<a href="https://aclanthology.org/2026.acl-long.2016/" target="_blank" rel="noopener">Paper (ACL 2026)</a>
|
| 153 |
+
</div>
|
| 154 |
+
<div class="banner" id="banner">
|
| 155 |
+
<b>Provenance:</b> reference testbed run — Qwen3-4B backbone on the 150-instance curated BFCL demo
|
| 156 |
+
subset (90 train / 30 sel / 30 test, seed 42, 3 epochs = 90 generations). The paper's reported
|
| 157 |
+
numbers use Qwen3-8B on the full benchmarks; this replay illustrates the mechanism, not the paper's scores.
|
| 158 |
+
</div>
|
| 159 |
+
</header>
|
| 160 |
+
|
| 161 |
+
<div id="error">
|
| 162 |
+
Replay data failed to load. This page needs <code>static/replay_data.js</code> next to it
|
| 163 |
+
(shipped in the repo under <code>docs/static/</code>). If you are viewing a copy of this file alone,
|
| 164 |
+
grab the full <code>docs/</code> directory from the
|
| 165 |
+
<a href="https://github.com/SYang2000/ACL_2026_EvoTool" target="_blank" rel="noopener">GitHub repo</a>.
|
| 166 |
+
</div>
|
| 167 |
+
|
| 168 |
+
<noscript><p style="max-width:720px;margin:24px auto;padding:12px 16px;background:#fef3c7;border:1px solid #d97706;border-radius:8px;font-size:14px">This interactive replay requires JavaScript. The same run is documented statically in <a href="https://github.com/SYang2000/ACL_2026_EvoTool/blob/main/CASE_STUDY.md">CASE_STUDY.md</a>.</p></noscript>
|
| 169 |
+
<div id="app" style="display:none">
|
| 170 |
+
<div class="card">
|
| 171 |
+
<div class="controls">
|
| 172 |
+
<button id="btnPrev" title="previous generation (←)">◀ Prev</button>
|
| 173 |
+
<button id="btnPlay" title="auto-advance">▶ Play</button>
|
| 174 |
+
<button id="btnNext" title="next generation (→)">Next ▶</button>
|
| 175 |
+
<input type="range" id="slider" min="0" max="89" value="0" step="1">
|
| 176 |
+
<span class="genlabel" id="genLabel">gen 0 / 89</span>
|
| 177 |
+
</div>
|
| 178 |
+
<div class="hint">Keyboard: ← / → step through generations, space toggles play.</div>
|
| 179 |
+
</div>
|
| 180 |
+
|
| 181 |
+
<div class="card">
|
| 182 |
+
<div class="charthead">Best validation score on S<sub>sel</sub> across generations
|
| 183 |
+
(dots: held-out test mean reward at epoch ends; dotted lines: epoch boundaries)</div>
|
| 184 |
+
<canvas id="chart" width="940" height="190"></canvas>
|
| 185 |
+
</div>
|
| 186 |
+
|
| 187 |
+
<div class="infonote">
|
| 188 |
+
<b>Why is the parent so often <code>init</code>?</b> Diversity-aware selection retains every policy
|
| 189 |
+
that wins at least one selection instance — including the initial policy — and parents are sampled
|
| 190 |
+
proportional to instance wins, so <code>init</code> keeps being re-selected as long as it still wins
|
| 191 |
+
instances. Deep chains form when an evolved policy overtakes it (e.g. the deployed
|
| 192 |
+
<code>g48-planner</code> lineage).
|
| 193 |
+
</div>
|
| 194 |
+
|
| 195 |
+
<div class="card" id="genPanel">
|
| 196 |
+
<div class="genhead">
|
| 197 |
+
<span class="gid" id="gid"></span>
|
| 198 |
+
<span class="chip" id="moduleChip"></span>
|
| 199 |
+
<span class="badge" id="acceptBadge"></span>
|
| 200 |
+
</div>
|
| 201 |
+
<div class="stats">
|
| 202 |
+
<span><span class="k">lineage </span><span class="v" id="lineage"></span></span>
|
| 203 |
+
<span><span class="k">batch reward </span><span class="v" id="rewards"></span></span>
|
| 204 |
+
<span><span class="k">best val (S<sub>sel</sub>) </span><span class="v" id="valScore"></span></span>
|
| 205 |
+
<span id="testWrap" style="display:none"><span class="k">held-out test </span><span class="v" id="testScore"></span></span>
|
| 206 |
+
<span><span class="k">population </span><span class="v" id="popSize"></span></span>
|
| 207 |
+
</div>
|
| 208 |
+
<div class="diag"><span class="who">BLAMER DIAGNOSIS</span><span id="diag"></span></div>
|
| 209 |
+
<pre class="diff" id="diff"></pre>
|
| 210 |
+
</div>
|
| 211 |
+
|
| 212 |
+
<footer>
|
| 213 |
+
Data source: <code>static/replay_data.js</code>, trimmed from the raw run log of the reference
|
| 214 |
+
testbed run (one JSONL record per generation, including rejected mutations).
|
| 215 |
+
</footer>
|
| 216 |
+
</div>
|
| 217 |
+
</div>
|
| 218 |
+
|
| 219 |
+
<script src="static/replay_data.js"></script>
|
| 220 |
+
<script>
|
| 221 |
+
(function () {
|
| 222 |
+
"use strict";
|
| 223 |
+
|
| 224 |
+
var D = window.REPLAY_DATA;
|
| 225 |
+
if (!D || !D.gens || !D.gens.length) {
|
| 226 |
+
document.getElementById("error").style.display = "block";
|
| 227 |
+
return;
|
| 228 |
+
}
|
| 229 |
+
document.getElementById("app").style.display = "block";
|
| 230 |
+
|
| 231 |
+
var gens = D.gens;
|
| 232 |
+
var N = gens.length;
|
| 233 |
+
var idx = 0;
|
| 234 |
+
var playing = false;
|
| 235 |
+
var timer = null;
|
| 236 |
+
|
| 237 |
+
var slider = document.getElementById("slider");
|
| 238 |
+
slider.max = N - 1;
|
| 239 |
+
|
| 240 |
+
function esc(s) {
|
| 241 |
+
return String(s == null ? "" : s)
|
| 242 |
+
.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
| 243 |
+
}
|
| 244 |
+
function fmt(x) { return (x == null) ? "–" : Number(x).toFixed(4); }
|
| 245 |
+
|
| 246 |
+
function renderDiff(text) {
|
| 247 |
+
if (!text) return "<span class='filehdr'>(no prompt change recorded)</span>";
|
| 248 |
+
return text.split("\n").map(function (line) {
|
| 249 |
+
var e = esc(line);
|
| 250 |
+
if (line.indexOf("---") === 0 || line.indexOf("+++") === 0) return "<span class='filehdr'>" + e + "</span>";
|
| 251 |
+
if (line.indexOf("@@") === 0) return "<span class='hunk'>" + e + "</span>";
|
| 252 |
+
if (line.charAt(0) === "+") return "<span class='add'>" + e + "</span>";
|
| 253 |
+
if (line.charAt(0) === "-") return "<span class='del'>" + e + "</span>";
|
| 254 |
+
return e;
|
| 255 |
+
}).join("\n");
|
| 256 |
+
}
|
| 257 |
+
|
| 258 |
+
// ---------- chart ----------
|
| 259 |
+
var canvas = document.getElementById("chart");
|
| 260 |
+
var ctx = canvas.getContext("2d");
|
| 261 |
+
var PAD = { l: 44, r: 12, t: 10, b: 24 };
|
| 262 |
+
|
| 263 |
+
function fitCanvas() {
|
| 264 |
+
var cssW = canvas.clientWidth || 940;
|
| 265 |
+
var scale = window.devicePixelRatio || 1;
|
| 266 |
+
canvas.width = Math.round(cssW * scale);
|
| 267 |
+
canvas.height = Math.round(190 * scale);
|
| 268 |
+
ctx.setTransform(scale, 0, 0, scale, 0, 0);
|
| 269 |
+
return { w: cssW, h: 190 };
|
| 270 |
+
}
|
| 271 |
+
|
| 272 |
+
function drawChart() {
|
| 273 |
+
var dim = fitCanvas();
|
| 274 |
+
var w = dim.w, h = dim.h;
|
| 275 |
+
var vals = gens.map(function (g) { return g.val; });
|
| 276 |
+
var lo = 0, hi = Math.max.apply(null, vals.concat(gens.filter(function (g) { return g.test != null; })
|
| 277 |
+
.map(function (g) { return g.test; }))) * 1.15 || 1;
|
| 278 |
+
function X(g) { return PAD.l + (w - PAD.l - PAD.r) * (g / (N - 1)); }
|
| 279 |
+
function Y(v) { return h - PAD.b - (h - PAD.b - PAD.t) * ((v - lo) / (hi - lo)); }
|
| 280 |
+
|
| 281 |
+
ctx.clearRect(0, 0, w, h);
|
| 282 |
+
// gridlines + y labels
|
| 283 |
+
ctx.font = "10px sans-serif";
|
| 284 |
+
ctx.fillStyle = "#5b6a7c";
|
| 285 |
+
ctx.strokeStyle = "#e4e9f0";
|
| 286 |
+
ctx.lineWidth = 1;
|
| 287 |
+
[0, 0.2, 0.4, 0.6].forEach(function (v) {
|
| 288 |
+
if (v > hi) return;
|
| 289 |
+
ctx.beginPath(); ctx.moveTo(PAD.l, Y(v)); ctx.lineTo(w - PAD.r, Y(v)); ctx.stroke();
|
| 290 |
+
ctx.fillText(v.toFixed(1), 14, Y(v) + 3);
|
| 291 |
+
});
|
| 292 |
+
// epoch boundaries (start of epochs 1 and 2)
|
| 293 |
+
ctx.strokeStyle = "#9aa7b6";
|
| 294 |
+
ctx.setLineDash([2, 3]);
|
| 295 |
+
for (var i = 1; i < N; i++) {
|
| 296 |
+
if (gens[i].epoch !== gens[i - 1].epoch) {
|
| 297 |
+
ctx.beginPath(); ctx.moveTo(X(gens[i].gen), PAD.t); ctx.lineTo(X(gens[i].gen), h - PAD.b); ctx.stroke();
|
| 298 |
+
}
|
| 299 |
+
}
|
| 300 |
+
ctx.setLineDash([]);
|
| 301 |
+
// x labels
|
| 302 |
+
[0, 30, 60, 89].forEach(function (g) {
|
| 303 |
+
ctx.fillText(String(g), X(g) - 6, h - 8);
|
| 304 |
+
});
|
| 305 |
+
// val line
|
| 306 |
+
ctx.strokeStyle = "#1f5fbf";
|
| 307 |
+
ctx.lineWidth = 2;
|
| 308 |
+
ctx.beginPath();
|
| 309 |
+
gens.forEach(function (g, i) {
|
| 310 |
+
var x = X(g.gen), y = Y(g.val);
|
| 311 |
+
if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
|
| 312 |
+
});
|
| 313 |
+
ctx.stroke();
|
| 314 |
+
// test points
|
| 315 |
+
ctx.fillStyle = "#c62828";
|
| 316 |
+
gens.forEach(function (g) {
|
| 317 |
+
if (g.test == null) return;
|
| 318 |
+
ctx.beginPath(); ctx.arc(X(g.gen), Y(g.test), 4.5, 0, 2 * Math.PI); ctx.fill();
|
| 319 |
+
});
|
| 320 |
+
// cursor
|
| 321 |
+
var cg = gens[idx];
|
| 322 |
+
ctx.strokeStyle = "#c96a11";
|
| 323 |
+
ctx.lineWidth = 1.5;
|
| 324 |
+
ctx.beginPath(); ctx.moveTo(X(cg.gen), PAD.t); ctx.lineTo(X(cg.gen), h - PAD.b); ctx.stroke();
|
| 325 |
+
ctx.fillStyle = "#c96a11";
|
| 326 |
+
ctx.beginPath(); ctx.arc(X(cg.gen), Y(cg.val), 5, 0, 2 * Math.PI); ctx.fill();
|
| 327 |
+
}
|
| 328 |
+
|
| 329 |
+
// ---------- panel ----------
|
| 330 |
+
function render() {
|
| 331 |
+
var g = gens[idx];
|
| 332 |
+
slider.value = idx;
|
| 333 |
+
document.getElementById("genLabel").textContent = "gen " + g.gen + " / " + (N - 1);
|
| 334 |
+
document.getElementById("gid").textContent = "Generation " + g.gen + " (epoch " + g.epoch + ")";
|
| 335 |
+
|
| 336 |
+
var chip = document.getElementById("moduleChip");
|
| 337 |
+
chip.textContent = "blamed: " + g.blamed;
|
| 338 |
+
chip.className = "chip " + g.blamed;
|
| 339 |
+
|
| 340 |
+
var badge = document.getElementById("acceptBadge");
|
| 341 |
+
badge.textContent = g.accepted ? "ACCEPTED" : "rejected";
|
| 342 |
+
badge.className = "badge " + (g.accepted ? "accepted" : "rejected");
|
| 343 |
+
|
| 344 |
+
document.getElementById("lineage").textContent = g.parent_id + " → " + g.child_id;
|
| 345 |
+
var delta = g.c - g.p;
|
| 346 |
+
var cls = delta > 0 ? "up" : (delta < 0 ? "down" : "");
|
| 347 |
+
document.getElementById("rewards").innerHTML =
|
| 348 |
+
fmt(g.p) + " → <span class='" + cls + "'>" + fmt(g.c) + "</span>";
|
| 349 |
+
document.getElementById("valScore").textContent = fmt(g.val);
|
| 350 |
+
var tw = document.getElementById("testWrap");
|
| 351 |
+
if (g.test != null) {
|
| 352 |
+
tw.style.display = "";
|
| 353 |
+
document.getElementById("testScore").textContent = fmt(g.test) + " (epoch end)";
|
| 354 |
+
} else {
|
| 355 |
+
tw.style.display = "none";
|
| 356 |
+
}
|
| 357 |
+
document.getElementById("popSize").textContent = g.pop;
|
| 358 |
+
document.getElementById("diag").textContent = g.diag || "(no diagnosis recorded)";
|
| 359 |
+
document.getElementById("diff").innerHTML = renderDiff(g.diff);
|
| 360 |
+
drawChart();
|
| 361 |
+
}
|
| 362 |
+
|
| 363 |
+
function goto(i) {
|
| 364 |
+
idx = Math.max(0, Math.min(N - 1, i));
|
| 365 |
+
render();
|
| 366 |
+
}
|
| 367 |
+
function stop() {
|
| 368 |
+
playing = false;
|
| 369 |
+
if (timer) { clearInterval(timer); timer = null; }
|
| 370 |
+
document.getElementById("btnPlay").innerHTML = "▶ Play";
|
| 371 |
+
}
|
| 372 |
+
function togglePlay() {
|
| 373 |
+
if (playing) { stop(); return; }
|
| 374 |
+
playing = true;
|
| 375 |
+
document.getElementById("btnPlay").innerHTML = "❚❚ Pause";
|
| 376 |
+
timer = setInterval(function () {
|
| 377 |
+
if (idx >= N - 1) { stop(); return; }
|
| 378 |
+
goto(idx + 1);
|
| 379 |
+
}, 1200);
|
| 380 |
+
}
|
| 381 |
+
|
| 382 |
+
document.getElementById("btnPrev").addEventListener("click", function () { stop(); goto(idx - 1); });
|
| 383 |
+
document.getElementById("btnNext").addEventListener("click", function () { stop(); goto(idx + 1); });
|
| 384 |
+
document.getElementById("btnPlay").addEventListener("click", togglePlay);
|
| 385 |
+
slider.addEventListener("input", function () { stop(); goto(parseInt(slider.value, 10)); });
|
| 386 |
+
document.addEventListener("keydown", function (e) {
|
| 387 |
+
if (e.key === "ArrowLeft") { stop(); goto(idx - 1); e.preventDefault(); }
|
| 388 |
+
else if (e.key === "ArrowRight") { stop(); goto(idx + 1); e.preventDefault(); }
|
| 389 |
+
else if (e.key === " ") { togglePlay(); e.preventDefault(); }
|
| 390 |
+
});
|
| 391 |
+
window.addEventListener("resize", drawChart);
|
| 392 |
+
|
| 393 |
+
render();
|
| 394 |
+
})();
|
| 395 |
+
</script>
|
| 396 |
+
</body>
|
| 397 |
</html>
|
static/replay_data.js
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|