Rahmath1/self_improving_agent
0
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.0">6<title>Self Improvement Agents โ Training Dashboard</title>7<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.min.js"></script>8<style>9 @import url('https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=Syne:wght@400;600;800&display=swap');10 11 :root {12 --bg: #050810;13 --surface: #0d1220;14 --surface2: #111827;15 --border: #1e2d45;16 --accent: #00d4ff;17 --accent2: #7c3aed;18 --green: #10b981;19 --orange: #f59e0b;20 --red: #ef4444;21 --text: #e2e8f0;22 --muted: #64748b;23 --glow: 0 0 20px rgba(0,212,255,0.15);24 }25 26 * { box-sizing: border-box; margin: 0; padding: 0; }27 28 body {29 font-family: 'Syne', sans-serif;30 background: var(--bg);31 color: var(--text);32 min-height: 100vh;33 padding: 24px;34 background-image:35 radial-gradient(ellipse 80% 50% at 20% 10%, rgba(0,212,255,0.04) 0%, transparent 60%),36 radial-gradient(ellipse 60% 40% at 80% 80%, rgba(124,58,237,0.05) 0%, transparent 60%);37 }38 39 header {40 display: flex;41 align-items: center;42 justify-content: space-between;43 margin-bottom: 32px;44 padding-bottom: 20px;45 border-bottom: 1px solid var(--border);46 }47 48 .logo {49 font-family: 'Space Mono', monospace;50 font-size: 13px;51 color: var(--accent);52 letter-spacing: 2px;53 text-transform: uppercase;54 }55 56 h1 {57 font-size: 28px;58 font-weight: 800;59 background: linear-gradient(135deg, #fff 30%, var(--accent));60 -webkit-background-clip: text;61 -webkit-text-fill-color: transparent;62 margin-top: 4px;63 }64 65 .subtitle {66 font-size: 13px;67 color: var(--muted);68 margin-top: 4px;69 font-family: 'Space Mono', monospace;70 }71 72 .status-pill {73 display: flex;74 align-items: center;75 gap: 8px;76 background: rgba(16,185,129,0.1);77 border: 1px solid rgba(16,185,129,0.3);78 padding: 8px 16px;79 border-radius: 20px;80 font-size: 12px;81 color: var(--green);82 font-family: 'Space Mono', monospace;83 }84 85 .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--green);86 animation: blink 1.5s infinite; }87 @keyframes blink { 0%,100%{opacity:1} 50%{opacity:0.3} }88 89 /* Controls */90 .controls {91 display: flex;92 gap: 12px;93 margin-bottom: 28px;94 flex-wrap: wrap;95 align-items: center;96 }97 98 .btn {99 font-family: 'Space Mono', monospace;100 font-size: 12px;101 padding: 10px 20px;102 border-radius: 8px;103 border: none;104 cursor: pointer;105 transition: all 0.2s;106 letter-spacing: 1px;107 text-transform: uppercase;108 }109 110 .btn-primary {111 background: var(--accent);112 color: #000;113 font-weight: 700;114 }115 .btn-primary:hover { box-shadow: 0 0 20px rgba(0,212,255,0.4); transform: translateY(-1px); }116 117 .btn-secondary {118 background: transparent;119 color: var(--text);120 border: 1px solid var(--border);121 }122 .btn-secondary:hover { border-color: var(--accent); color: var(--accent); }123 124 .btn-danger {125 background: transparent;126 color: var(--red);127 border: 1px solid rgba(239,68,68,0.3);128 }129 .btn-danger:hover { background: rgba(239,68,68,0.1); }130 131 select {132 font-family: 'Space Mono', monospace;133 font-size: 12px;134 background: var(--surface);135 color: var(--text);136 border: 1px solid var(--border);137 padding: 10px 16px;138 border-radius: 8px;139 cursor: pointer;140 }141 142 /* Metric cards */143 .metrics-row {144 display: grid;145 grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));146 gap: 16px;147 margin-bottom: 28px;148 }149 150 .metric-card {151 background: var(--surface);152 border: 1px solid var(--border);153 border-radius: 12px;154 padding: 20px;155 position: relative;156 overflow: hidden;157 transition: border-color 0.3s;158 }159 .metric-card:hover { border-color: var(--accent); box-shadow: var(--glow); }160 .metric-card::before {161 content: '';162 position: absolute;163 top: 0; left: 0; right: 0;164 height: 2px;165 background: linear-gradient(90deg, var(--accent), var(--accent2));166 }167 168 .metric-label {169 font-size: 11px;170 color: var(--muted);171 text-transform: uppercase;172 letter-spacing: 1.5px;173 font-family: 'Space Mono', monospace;174 margin-bottom: 10px;175 }176 177 .metric-val {178 font-size: 32px;179 font-weight: 800;180 line-height: 1;181 }182 183 .metric-val.good { color: var(--green); }184 .metric-val.warn { color: var(--orange); }185 .metric-val.bad { color: var(--red); }186 .metric-val.blue { color: var(--accent); }187 188 .metric-delta {189 font-size: 12px;190 margin-top: 6px;191 font-family: 'Space Mono', monospace;192 }193 .metric-delta.up { color: var(--green); }194 .metric-delta.down { color: var(--red); }195 196 /* Chart grid */197 .charts-grid {198 display: grid;199 grid-template-columns: 2fr 1fr;200 gap: 20px;201 margin-bottom: 20px;202 }203 204 .charts-bottom {205 display: grid;206 grid-template-columns: 1fr 1fr 1fr;207 gap: 20px;208 margin-bottom: 20px;209 }210 211 .card {212 background: var(--surface);213 border: 1px solid var(--border);214 border-radius: 12px;215 padding: 20px;216 }217 218 .card-title {219 font-size: 12px;220 color: var(--muted);221 text-transform: uppercase;222 letter-spacing: 1.5px;223 font-family: 'Space Mono', monospace;224 margin-bottom: 16px;225 display: flex;226 align-items: center;227 justify-content: space-between;228 }229 230 .card-badge {231 font-size: 10px;232 padding: 2px 8px;233 border-radius: 10px;234 background: rgba(0,212,255,0.1);235 color: var(--accent);236 border: 1px solid rgba(0,212,255,0.2);237 }238 239 canvas { max-height: 260px; }240 241 /* Comparison table */242 .comparison-table {243 width: 100%;244 border-collapse: collapse;245 font-family: 'Space Mono', monospace;246 font-size: 13px;247 }248 249 .comparison-table th {250 padding: 12px 16px;251 text-align: left;252 font-size: 11px;253 color: var(--muted);254 text-transform: uppercase;255 letter-spacing: 1px;256 border-bottom: 1px solid var(--border);257 }258 259 .comparison-table td {260 padding: 14px 16px;261 border-bottom: 1px solid rgba(30,45,69,0.5);262 }263 264 .comparison-table tr:last-child td { border-bottom: none; }265 266 .comparison-table tr:hover td { background: rgba(255,255,255,0.02); }267 268 .val-base { color: var(--orange); }269 .val-ft { color: var(--green); }270 .val-up { color: var(--green); }271 .val-down { color: var(--red); }272 273 /* Curriculum level bar */274 .level-bar {275 display: flex;276 gap: 6px;277 margin-top: 12px;278 }279 280 .level-dot {281 flex: 1;282 height: 6px;283 border-radius: 3px;284 background: var(--border);285 transition: background 0.5s;286 }287 .level-dot.active { background: var(--accent); }288 .level-dot.done { background: var(--green); }289 290 /* Episode log */291 .episode-log {292 max-height: 200px;293 overflow-y: auto;294 font-family: 'Space Mono', monospace;295 font-size: 11px;296 }297 298 .log-entry {299 display: flex;300 justify-content: space-between;301 align-items: center;302 padding: 6px 0;303 border-bottom: 1px solid rgba(30,45,69,0.4);304 }305 306 .log-entry:last-child { border-bottom: none; }307 308 .log-task { color: var(--muted); }309 .log-score { font-weight: 700; }310 .log-badge {311 font-size: 10px;312 padding: 2px 6px;313 border-radius: 8px;314 }315 .log-badge.promoted { background: rgba(16,185,129,0.15); color: var(--green); border: 1px solid rgba(16,185,129,0.3); }316 .log-badge.normal { background: rgba(100,116,139,0.15); color: var(--muted); }317 .log-badge.penalty { background: rgba(239,68,68,0.15); color: var(--red); }318 319 /* Scrollbar */320 ::-webkit-scrollbar { width: 4px; }321 ::-webkit-scrollbar-track { background: transparent; }322 ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }323 324 .phase-toggle {325 display: flex;326 gap: 0;327 border: 1px solid var(--border);328 border-radius: 8px;329 overflow: hidden;330 }331 .phase-btn {332 padding: 8px 16px;333 font-family: 'Space Mono', monospace;334 font-size: 11px;335 cursor: pointer;336 border: none;337 background: transparent;338 color: var(--muted);339 transition: all 0.2s;340 }341 .phase-btn.active { background: var(--accent); color: #000; font-weight: 700; }342</style>343</head>344<body>345 346<header>347 <div>348 <div class="logo">EDA OpenEnv ยท Self-Improvement System</div>349 <h1>Training Dashboard</h1>350 <div class="subtitle">// Base vs Fine-tuned ยท Curriculum Progression ยท Injection Detection</div>351 </div>352 <div class="status-pill">353 <div class="dot"></div>354 Live Training355 </div>356</header>357 358<!-- Controls -->359<div class="controls">360 <button class="btn btn-primary" onclick="runSimulation()">โถ Simulate Training</button>361 <button class="btn btn-secondary" onclick="resetAll()">โบ Reset</button>362 <select id="speedSelect">363 <option value="300">Speed: Fast</option>364 <option value="600" selected>Speed: Normal</option>365 <option value="1200">Speed: Slow</option>366 </select>367 <div class="phase-toggle">368 <button class="phase-btn active" onclick="setView('both')" id="btn-both">Both Phases</button>369 <button class="phase-btn" onclick="setView('base')" id="btn-base">Base Only</button>370 <button class="phase-btn" onclick="setView('ft')" id="btn-ft">Fine-tuned Only</button>371 </div>372</div>373 374<!-- Metrics Row -->375<div class="metrics-row">376 <div class="metric-card">377 <div class="metric-label">Base Avg Reward</div>378 <div class="metric-val warn" id="baseReward">โ</div>379 <div class="metric-delta" id="baseEps">0 episodes</div>380 </div>381 <div class="metric-card">382 <div class="metric-label">Fine-tuned Avg Reward</div>383 <div class="metric-val good" id="ftReward">โ</div>384 <div class="metric-delta up" id="ftDelta">โ</div>385 </div>386 <div class="metric-card">387 <div class="metric-label">Injection Catch Rate</div>388 <div class="metric-val blue" id="catchRate">โ</div>389 <div class="metric-delta" id="catchDelta">โ</div>390 </div>391 <div class="metric-card">392 <div class="metric-label">Curriculum Level</div>393 <div class="metric-val blue" id="currLevel">1</div>394 <div class="level-bar">395 <div class="level-dot active" id="l1"></div>396 <div class="level-dot" id="l2"></div>397 <div class="level-dot" id="l3"></div>398 <div class="level-dot" id="l4"></div>399 <div class="level-dot" id="l5"></div>400 </div>401 </div>402 <div class="metric-card">403 <div class="metric-label">Total Episodes</div>404 <div class="metric-val blue" id="totalEps">0</div>405 <div class="metric-delta" id="epsBreakdown">base: 0 ยท ft: 0</div>406 </div>407 <div class="metric-card">408 <div class="metric-label">Improvement</div>409 <div class="metric-val good" id="improvement">โ</div>410 <div class="metric-delta up" id="improvementNote">โ</div>411 </div>412</div>413 414<!-- Main Charts -->415<div class="charts-grid">416 <div class="card">417 <div class="card-title">418 Reward Progression419 <span class="card-badge">per episode</span>420 </div>421 <canvas id="rewardChart"></canvas>422 </div>423 <div class="card">424 <div class="card-title">425 Curriculum Levels426 <span class="card-badge">fine-tuned</span>427 </div>428 <canvas id="levelChart"></canvas>429 </div>430</div>431 432<!-- Bottom Charts -->433<div class="charts-bottom">434 <div class="card">435 <div class="card-title">Injection Detection Rate</div>436 <canvas id="injectionChart"></canvas>437 </div>438 <div class="card">439 <div class="card-title">Penalties per Episode</div>440 <canvas id="penaltyChart"></canvas>441 </div>442 <div class="card">443 <div class="card-title">Task Score by Type</div>444 <canvas id="taskChart"></canvas>445 </div>446</div>447 448<!-- Comparison Table + Episode Log -->449<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-bottom:20px">450 <div class="card">451 <div class="card-title">BASE vs FINE-TUNED Comparison</div>452 <table class="comparison-table" id="compTable">453 <tr>454 <th>Metric</th>455 <th>Base</th>456 <th>Fine-tuned</th>457 <th>Delta</th>458 </tr>459 <tr>460 <td>Avg Reward</td>461 <td class="val-base" id="ct-base-reward">โ</td>462 <td class="val-ft" id="ct-ft-reward">โ</td>463 <td class="val-up" id="ct-delta-reward">โ</td>464 </tr>465 <tr>466 <td>Inj Catch Rate</td>467 <td class="val-base" id="ct-base-catch">โ</td>468 <td class="val-ft" id="ct-ft-catch">โ</td>469 <td class="val-up" id="ct-delta-catch">โ</td>470 </tr>471 <tr>472 <td>Inj Executed</td>473 <td class="val-base" id="ct-base-inj">โ</td>474 <td class="val-ft" id="ct-ft-inj">โ</td>475 <td id="ct-delta-inj">โ</td>476 </tr>477 <tr>478 <td>False Positives</td>479 <td class="val-base" id="ct-base-fp">โ</td>480 <td class="val-ft" id="ct-ft-fp">โ</td>481 <td id="ct-delta-fp">โ</td>482 </tr>483 <tr>484 <td>Penalties</td>485 <td class="val-base" id="ct-base-pen">โ</td>486 <td class="val-ft" id="ct-ft-pen">โ</td>487 <td class="val-up" id="ct-delta-pen">โ</td>488 </tr>489 </table>490 </div>491 <div class="card">492 <div class="card-title">Episode Log</div>493 <div class="episode-log" id="episodeLog">494 <div style="color:var(--muted);text-align:center;padding:20px;font-size:11px;font-family:Space Mono">495 Click "Simulate Training" to begin496 </div>497 </div>498 </div>499</div>500 501<script>502// โโโ Chart setup โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ503const CHART_DEFAULTS = {504 responsive: true,505 maintainAspectRatio: true,506 plugins: { legend: { labels: { color: '#64748b', font: { family: 'Space Mono', size: 11 } } } },507 scales: {508 x: { ticks: { color: '#64748b', font: { family: 'Space Mono', size: 10 } }, grid: { color: 'rgba(30,45,69,0.5)' } },509 y: { ticks: { color: '#64748b', font: { family: 'Space Mono', size: 10 } }, grid: { color: 'rgba(30,45,69,0.5)' } },510 }511};512 513const rewardChart = new Chart(document.getElementById('rewardChart'), {514 type: 'line',515 data: {516 labels: [],517 datasets: [518 { label: 'Base Model', data: [], borderColor: '#f59e0b', backgroundColor: 'rgba(245,158,11,0.08)', tension: 0.4, pointRadius: 4, pointBackgroundColor: '#f59e0b' },519 { label: 'Fine-tuned', data: [], borderColor: '#00d4ff', backgroundColor: 'rgba(0,212,255,0.08)', tension: 0.4, pointRadius: 4, pointBackgroundColor: '#00d4ff' },520 { label: 'FT Rolling Avg', data: [], borderColor: '#10b981', borderDash: [5,3], backgroundColor: 'transparent', tension: 0.4, pointRadius: 0 },521 ]522 },523 options: { ...CHART_DEFAULTS, scales: { ...CHART_DEFAULTS.scales, y: { ...CHART_DEFAULTS.scales.y, min: 0, max: 1 } } }524});525 526const levelChart = new Chart(document.getElementById('levelChart'), {527 type: 'bar',528 data: { labels: ['๐ฑ Novice','๐ Learner','๐ง Practitioner','๐ Expert','๐ Master'],529 datasets: [{ label: 'Episodes at Level', data: [0,0,0,0,0], backgroundColor: ['#1e3a5f','#1a4a6b','#0e6ea3','#0090cc','#00d4ff'], borderRadius: 6 }] },530 options: { ...CHART_DEFAULTS, plugins: { legend: { display: false } } }531});532 533const injectionChart = new Chart(document.getElementById('injectionChart'), {534 type: 'line',535 data: {536 labels: [],537 datasets: [538 { label: 'Base Catch %', data: [], borderColor: '#f59e0b', tension: 0.4, pointRadius: 3 },539 { label: 'FT Catch %', data: [], borderColor: '#10b981', tension: 0.4, pointRadius: 3 },540 ]541 },542 options: { ...CHART_DEFAULTS, scales: { ...CHART_DEFAULTS.scales, y: { ...CHART_DEFAULTS.scales.y, min: 0, max: 100 } } }543});544 545const penaltyChart = new Chart(document.getElementById('penaltyChart'), {546 type: 'bar',547 data: {548 labels: [],549 datasets: [550 { label: 'Base', data: [], backgroundColor: 'rgba(245,158,11,0.5)', borderRadius: 4 },551 { label: 'Fine-tuned', data: [], backgroundColor: 'rgba(0,212,255,0.5)', borderRadius: 4 },552 ]553 },554 options: CHART_DEFAULTS555});556 557const taskChart = new Chart(document.getElementById('taskChart'), {558 type: 'radar',559 data: {560 labels: ['detect_missing','find_correlation','generate_insight'],561 datasets: [562 { label: 'Base', data: [0,0,0], borderColor: '#f59e0b', backgroundColor: 'rgba(245,158,11,0.1)', pointBackgroundColor: '#f59e0b' },563 { label: 'Fine-tuned', data: [0,0,0], borderColor: '#00d4ff', backgroundColor: 'rgba(0,212,255,0.1)', pointBackgroundColor: '#00d4ff' },564 ]565 },566 options: {567 responsive: true,568 plugins: { legend: { labels: { color: '#64748b', font: { family: 'Space Mono', size: 11 } } } },569 scales: { r: { min: 0, max: 1, ticks: { color: '#64748b', stepSize: 0.2, font: { size: 9 } }, grid: { color: 'rgba(30,45,69,0.5)' }, pointLabels: { color: '#94a3b8', font: { family: 'Space Mono', size: 10 } } } }570 }571});572 573// โโโ Simulation State โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ574let simTimer = null;575let state = {576 episode: 0,577 baseScores: [], ftScores: [],578 basePenalties: [], ftPenalties: [],579 baseCatch: [], ftCatch: [],580 taskScores: { base: {dm:0,fc:0,gi:0,n:0}, ft: {dm:0,fc:0,gi:0,n:0} },581 levelCounts: [0,0,0,0,0],582 currLevel: 1,583 baseInjExecuted: 0, ftInjExecuted: 0,584 baseFP: 0, ftFP: 0,585 currentView: 'both',586 phase: 'base',587 phaseEps: 0,588};589 590const TASKS = ['detect_missing','find_correlation','generate_insight'];591const LEVEL_NAMES = ['๐ฑ Novice','๐ Learner','๐ง Practitioner','๐ Expert','๐ Master'];592 593// Simulate realistic score curve for base vs fine-tuned594function genBaseScore(ep) {595 const base = 0.28 + Math.random() * 0.15;596 return Math.max(0.05, Math.min(0.98, base + (Math.random()-0.5)*0.08));597}598 599function genFTScore(ep, level) {600 // Fine-tuned model improves over episodes with curriculum601 const base = 0.35 + (ep / 40) * 0.45 + (level-1) * 0.06;602 return Math.max(0.10, Math.min(0.98, base + (Math.random()-0.5)*0.06));603}604 605function rollingAvg(arr, n=5) {606 if (!arr.length) return 0;607 const slice = arr.slice(-n);608 return slice.reduce((a,b)=>a+b,0)/slice.length;609}610 611function runSimulation() {612 if (simTimer) return;613 const speed = parseInt(document.getElementById('speedSelect').value);614 simTimer = setInterval(simulateStep, speed);615}616 617function simulateStep() {618 state.episode++;619 state.phaseEps++;620 const task = TASKS[(state.episode-1) % 3];621 622 if (state.episode <= 12) {623 // Phase 1: Base model624 state.phase = 'base';625 const score = genBaseScore(state.phaseEps);626 const penalty = Math.floor(Math.random() * 3);627 const catch_r = 60 + Math.random() * 20;628 const injExec = Math.random() < 0.15 ? 1 : 0;629 630 state.baseScores.push(score);631 state.basePenalties.push(penalty);632 state.baseCatch.push(catch_r);633 state.baseInjExecuted += injExec;634 if (Math.random() < 0.05) state.baseFP++;635 636 // Task scores637 const tk = task === 'detect_missing' ? 'dm' : task === 'find_correlation' ? 'fc' : 'gi';638 state.taskScores.base[tk] = (state.taskScores.base[tk] * state.taskScores.base.n + score) / (state.taskScores.base.n + 1);639 state.taskScores.base.n++;640 641 addLogEntry(state.episode, 'BASE', task, score, penalty, false);642 643 } else {644 // Phase 2: Fine-tuned model with curriculum645 state.phase = 'ft';646 const ftEp = state.phaseEps - 12;647 const score = genFTScore(ftEp, state.currLevel);648 const penalty = Math.max(0, Math.floor(Math.random() * 2) - (state.currLevel > 2 ? 1 : 0));649 const catch_r = Math.min(98, 70 + ftEp * 1.5 + Math.random() * 10);650 const injExec = Math.random() < Math.max(0.02, 0.12 - ftEp*0.008) ? 1 : 0;651 652 state.ftScores.push(score);653 state.ftPenalties.push(penalty);654 state.ftCatch.push(catch_r);655 state.ftInjExecuted += injExec;656 if (Math.random() < 0.03) state.ftFP++;657 658 // Task scores659 const tk = task === 'detect_missing' ? 'dm' : task === 'find_correlation' ? 'fc' : 'gi';660 state.taskScores.ft[tk] = (state.taskScores.ft[tk] * state.taskScores.ft.n + score) / (state.taskScores.ft.n + 1);661 state.taskScores.ft.n++;662 663 // Curriculum progression664 state.levelCounts[state.currLevel-1]++;665 const ftAvg = rollingAvg(state.ftScores, 5);666 const thresholds = [0, 0.45, 0.60, 0.75, 0.88];667 if (state.currLevel < 5 && ftAvg >= thresholds[state.currLevel]) {668 state.currLevel++;669 addLogEntry(state.episode, 'FT', task, score, penalty, true);670 } else {671 addLogEntry(state.episode, 'FT', task, score, penalty, false);672 }673 }674 675 updateCharts();676 updateMetrics();677 678 // Stop after 24 episodes679 if (state.episode >= 24) {680 clearInterval(simTimer);681 simTimer = null;682 document.querySelector('.dot').style.background = '#10b981';683 }684}685 686function addLogEntry(ep, phase, task, score, penalties, promoted) {687 const log = document.getElementById('episodeLog');688 const p = log.querySelector('p');689 if (p) p.remove();690 691 const entry = document.createElement('div');692 entry.className = 'log-entry';693 const badgeClass = promoted ? 'promoted' : (penalties > 0 ? 'penalty' : 'normal');694 const badgeText = promoted ? '๐ PROMOTED' : (penalties > 0 ? `${penalties} pen` : 'ok');695 const scoreColor = score > 0.6 ? 'var(--green)' : score > 0.35 ? 'var(--orange)' : 'var(--red)';696 entry.innerHTML = `697 <span class="log-task">Ep${ep} [${phase}] ${task.replace('_',' ')}</span>698 <span class="log-score" style="color:${scoreColor}">${score.toFixed(3)}</span>699 <span class="log-badge ${badgeClass}">${badgeText}</span>700 `;701 log.insertBefore(entry, log.firstChild);702}703 704function updateCharts() {705 const epLabels = Array.from({length: state.episode}, (_,i) => `Ep${i+1}`);706 707 // Reward chart โ pad shorter array708 const baseData = [...state.baseScores, ...new Array(Math.max(0, state.ftScores.length)).fill(null)];709 const ftData = [...new Array(state.baseScores.length).fill(null), ...state.ftScores];710 711 // Rolling avg for FT712 const ftRolling = state.ftScores.map((_, i) => {713 const slice = state.ftScores.slice(Math.max(0,i-4), i+1);714 return slice.reduce((a,b)=>a+b,0)/slice.length;715 });716 const ftRollingPadded = [...new Array(state.baseScores.length).fill(null), ...ftRolling];717 718 rewardChart.data.labels = epLabels;719 rewardChart.data.datasets[0].data = baseData;720 rewardChart.data.datasets[1].data = ftData;721 rewardChart.data.datasets[2].data = ftRollingPadded;722 rewardChart.update('none');723 724 // Level chart725 levelChart.data.datasets[0].data = [...state.levelCounts];726 levelChart.update('none');727 728 // Injection chart729 const injLabels = Array.from({length: state.episode}, (_,i) => `Ep${i+1}`);730 const baseCatchPadded = [...state.baseCatch, ...new Array(state.ftCatch.length).fill(null)];731 const ftCatchPadded = [...new Array(state.baseCatch.length).fill(null), ...state.ftCatch];732 injectionChart.data.labels = injLabels;733 injectionChart.data.datasets[0].data = baseCatchPadded;734 injectionChart.data.datasets[1].data = ftCatchPadded;735 injectionChart.update('none');736 737 // Penalty chart738 const allPenLabels = Array.from({length: state.episode}, (_,i) => `Ep${i+1}`);739 const basePenPadded = [...state.basePenalties, ...new Array(state.ftPenalties.length).fill(0)];740 const ftPenPadded = [...new Array(state.basePenalties.length).fill(0), ...state.ftPenalties];741 penaltyChart.data.labels = allPenLabels;742 penaltyChart.data.datasets[0].data = basePenPadded;743 penaltyChart.data.datasets[1].data = ftPenPadded;744 penaltyChart.update('none');745 746 // Task radar chart747 const ts = state.taskScores;748 taskChart.data.datasets[0].data = [ts.base.dm||0, ts.base.fc||0, ts.base.gi||0];749 taskChart.data.datasets[1].data = [ts.ft.dm||0, ts.ft.fc||0, ts.ft.gi||0];750 taskChart.update('none');751}752 753function updateMetrics() {754 const baseAvg = state.baseScores.length ? rollingAvg(state.baseScores, state.baseScores.length) : 0;755 const ftAvg = state.ftScores.length ? rollingAvg(state.ftScores, state.ftScores.length) : 0;756 const delta = ftAvg - baseAvg;757 758 // Cards759 document.getElementById('baseReward').textContent = state.baseScores.length ? baseAvg.toFixed(3) : 'โ';760 document.getElementById('baseEps').textContent = `${state.baseScores.length} episodes`;761 document.getElementById('ftReward').textContent = state.ftScores.length ? ftAvg.toFixed(3) : 'โ';762 document.getElementById('ftDelta').textContent = state.ftScores.length ? `${delta > 0 ? 'โ' : 'โ'} ${Math.abs(delta).toFixed(3)} vs base` : 'โ';763 document.getElementById('currLevel').textContent = state.currLevel;764 document.getElementById('totalEps').textContent = state.episode;765 document.getElementById('epsBreakdown').textContent = `base: ${state.baseScores.length} ยท ft: ${state.ftScores.length}`;766 document.getElementById('improvement').textContent = state.ftScores.length ? `${delta >= 0 ? '+' : ''}${delta.toFixed(3)}` : 'โ';767 document.getElementById('improvementNote').textContent = state.ftScores.length ? `${((delta/Math.max(baseAvg,0.01))*100).toFixed(0)}% improvement` : 'โ';768 769 // Injection metrics770 const baseCatchAvg = state.baseCatch.length ? state.baseCatch.reduce((a,b)=>a+b,0)/state.baseCatch.length : 0;771 const ftCatchAvg = state.ftCatch.length ? state.ftCatch.reduce((a,b)=>a+b,0)/state.ftCatch.length : 0;772 document.getElementById('catchRate').textContent = state.ftCatch.length ? `${ftCatchAvg.toFixed(0)}%` : 'โ';773 document.getElementById('catchDelta').textContent = state.ftCatch.length ? `base: ${baseCatchAvg.toFixed(0)}% ยท ft: ${ftCatchAvg.toFixed(0)}%` : 'โ';774 775 // Level dots776 for (let i = 1; i <= 5; i++) {777 const dot = document.getElementById(`l${i}`);778 dot.className = 'level-dot' + (i < state.currLevel ? ' done' : i === state.currLevel ? ' active' : '');779 }780 781 // Comparison table782 if (state.baseScores.length) {783 const basePenTotal = state.basePenalties.reduce((a,b)=>a+b,0);784 const ftPenTotal = state.ftPenalties.reduce((a,b)=>a+b,0);785 786 document.getElementById('ct-base-reward').textContent = `+${baseAvg.toFixed(2)}`;787 document.getElementById('ct-ft-reward').textContent = `+${ftAvg.toFixed(2)}`;788 document.getElementById('ct-delta-reward').textContent = `${delta >= 0 ? '+' : ''}${delta.toFixed(2)}`;789 document.getElementById('ct-delta-reward').className = delta >= 0 ? 'val-up' : 'val-down';790 791 document.getElementById('ct-base-catch').textContent = `${baseCatchAvg.toFixed(0)}%`;792 document.getElementById('ct-ft-catch').textContent = `${ftCatchAvg.toFixed(0)}%`;793 const catchDelta = ftCatchAvg - baseCatchAvg;794 document.getElementById('ct-delta-catch').textContent = `${catchDelta >= 0 ? '+' : ''}${catchDelta.toFixed(0)}%`;795 document.getElementById('ct-delta-catch').className = catchDelta >= 0 ? 'val-up' : 'val-down';796 797 document.getElementById('ct-base-inj').textContent = state.baseInjExecuted;798 document.getElementById('ct-ft-inj').textContent = state.ftInjExecuted;799 const injDelta = state.ftInjExecuted - state.baseInjExecuted;800 document.getElementById('ct-delta-inj').textContent = `${injDelta >= 0 ? '+' : ''}${injDelta}`;801 document.getElementById('ct-delta-inj').className = injDelta <= 0 ? 'val-up' : 'val-down';802 803 document.getElementById('ct-base-fp').textContent = state.baseFP;804 document.getElementById('ct-ft-fp').textContent = state.ftFP;805 const fpDelta = state.ftFP - state.baseFP;806 document.getElementById('ct-delta-fp').textContent = `${fpDelta >= 0 ? '+' : ''}${fpDelta}`;807 document.getElementById('ct-delta-fp').className = fpDelta <= 0 ? 'val-up' : 'val-down';808 809 document.getElementById('ct-base-pen').textContent = basePenTotal;810 document.getElementById('ct-ft-pen').textContent = ftPenTotal;811 const penDelta = ftPenTotal - basePenTotal;812 document.getElementById('ct-delta-pen').textContent = `${penDelta >= 0 ? '+' : ''}${penDelta}`;813 document.getElementById('ct-delta-pen').className = penDelta <= 0 ? 'val-up' : 'val-down';814 }815}816 817function resetAll() {818 clearInterval(simTimer); simTimer = null;819 state = {820 episode:0, baseScores:[], ftScores:[],821 basePenalties:[], ftPenalties:[],822 baseCatch:[], ftCatch:[],823 taskScores:{base:{dm:0,fc:0,gi:0,n:0},ft:{dm:0,fc:0,gi:0,n:0}},824 levelCounts:[0,0,0,0,0], currLevel:1,825 baseInjExecuted:0, ftInjExecuted:0, baseFP:0, ftFP:0,826 currentView:'both', phase:'base', phaseEps:0,827 };828 [rewardChart, levelChart, injectionChart, penaltyChart, taskChart].forEach(c => {829 c.data.labels = [];830 c.data.datasets.forEach(d => d.data = []);831 c.update();832 });833 levelChart.data.datasets[0].data = [0,0,0,0,0];834 levelChart.update();835 taskChart.data.datasets[0].data = [0,0,0];836 taskChart.data.datasets[1].data = [0,0,0];837 taskChart.update();838 839 ['baseReward','ftReward','catchRate','improvement'].forEach(id => document.getElementById(id).textContent = 'โ');840 document.getElementById('currLevel').textContent = '1';841 document.getElementById('totalEps').textContent = '0';842 for(let i=1;i<=5;i++){843 const d=document.getElementById(`l${i}`);844 d.className='level-dot'+(i===1?' active':'');845 }846 document.getElementById('episodeLog').innerHTML =847 '<div style="color:var(--muted);text-align:center;padding:20px;font-size:11px;font-family:Space Mono">Click "Simulate Training" to begin</div>';848}849 850function setView(v) {851 state.currentView = v;852 ['both','base','ft'].forEach(b => {853 document.getElementById(`btn-${b}`).classList.toggle('active', b===v);854 });855}856</script>857</body>858</html>