Mahathi4554/sql-query-debugging
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>SQL Query Debugging β OpenEnv</title>7 <link rel="preconnect" href="https://fonts.googleapis.com" />8 <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&family=DM+Sans:wght@300;400;500;600&display=swap" rel="stylesheet" />9 <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.min.js"></script>10 <style>11 :root {12 --bg: #0d0f14; --bg2: #13161e; --bg3: #1a1e2a; --bg4: #222636;13 --border: rgba(255,255,255,0.07); --border2: rgba(255,255,255,0.12);14 --text: #e8eaf0; --text2: #8b90a0; --text3: #555a6a;15 --accent: #4f9cf9; --accent2: #7c6af7;16 --green: #3dd68c; --amber: #f5a623; --red: #f05252;17 --easy: #3dd68c; --medium: #f5a623; --hard: #f05252; --expert: #b06cf5;18 --mono: "JetBrains Mono", monospace; --sans: "DM Sans", sans-serif;19 --radius: 10px; --radius-sm: 6px;20 }21 * { box-sizing: border-box; margin: 0; padding: 0; }22 body { font-family: var(--sans); background: var(--bg); color: var(--text); min-height: 100vh; font-size: 14px; line-height: 1.6; }23 .shell { display: grid; grid-template-columns: 260px 1fr; min-height: 100vh; }24 .sidebar { background: var(--bg2); border-right: 1px solid var(--border); padding: 24px 0; display: flex; flex-direction: column; position: sticky; top: 0; height: 100vh; overflow-y: auto; }25 .logo { padding: 0 20px 24px; border-bottom: 1px solid var(--border); margin-bottom: 16px; }26 .logo-title { font-family: var(--mono); font-size: 13px; font-weight: 700; color: var(--accent); letter-spacing: 0.05em; margin-bottom: 4px; }27 .logo-sub { font-size: 11px; color: var(--text3); letter-spacing: 0.02em; }28 .nav-section { padding: 0 12px; margin-bottom: 8px; }29 .nav-label { font-size: 10px; font-weight: 600; letter-spacing: 0.1em; color: var(--text3); text-transform: uppercase; padding: 0 8px; margin-bottom: 6px; }30 .nav-item { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: var(--radius-sm); cursor: pointer; color: var(--text2); font-size: 13px; font-weight: 400; transition: background 0.15s, color 0.15s; border: none; background: none; width: 100%; text-align: left; }31 .nav-item:hover { background: var(--bg3); color: var(--text); }32 .nav-item.active { background: rgba(79,156,249,0.12); color: var(--accent); }33 .nav-item .dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }34 .task-badge { margin-left: auto; font-size: 10px; font-family: var(--mono); padding: 2px 7px; border-radius: 20px; font-weight: 500; }35 .main { padding: 28px 32px; overflow-y: auto; }36 .page { display: none; }37 .page.active { display: block; }38 .page-header { margin-bottom: 24px; display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }39 .page-title { font-size: 22px; font-weight: 600; color: var(--text); margin-bottom: 4px; }40 .page-sub { font-size: 13px; color: var(--text2); }41 .card { background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius); padding: 20px; margin-bottom: 16px; }42 .card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; }43 .card-title { font-size: 13px; font-weight: 600; color: var(--text); letter-spacing: 0.01em; }44 .stats-grid { display: grid; grid-template-columns: repeat(4,1fr); gap: 12px; margin-bottom: 20px; }45 .stat-card { background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px 18px; }46 .stat-label { font-size: 11px; color: var(--text3); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 6px; }47 .stat-value { font-size: 26px; font-weight: 600; font-family: var(--mono); color: var(--text); line-height: 1; }48 .stat-sub { font-size: 11px; color: var(--text2); margin-top: 4px; }49 .task-grid { display: grid; grid-template-columns: repeat(auto-fill,minmax(300px,1fr)); gap: 14px; }50 .task-card { background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius); padding: 18px; cursor: pointer; transition: border-color 0.15s, background 0.15s; }51 .task-card:hover { border-color: var(--border2); background: var(--bg3); }52 .task-card.selected { border-color: var(--accent); background: rgba(79,156,249,0.06); }53 .task-card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }54 .task-name { font-family: var(--mono); font-size: 12px; font-weight: 700; color: var(--text); }55 .difficulty-badge { font-size: 10px; font-weight: 600; font-family: var(--mono); padding: 3px 8px; border-radius: 20px; text-transform: uppercase; letter-spacing: 0.05em; }56 .diff-easy { background: rgba(61,214,140,0.12); color: var(--easy); }57 .diff-medium { background: rgba(245,166,35,0.12); color: var(--medium); }58 .diff-hard { background: rgba(240,82,82,0.12); color: var(--hard); }59 .diff-expert { background: rgba(176,108,245,0.12); color: var(--expert); }60 .task-desc { font-size: 12px; color: var(--text2); line-height: 1.55; margin-bottom: 12px; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }61 .task-meta { display: flex; gap: 12px; font-size: 11px; color: var(--text3); font-family: var(--mono); }62 63 /* Tabs */64 .tab-bar { display: flex; gap: 2px; background: var(--bg3); border-radius: var(--radius-sm); padding: 3px; margin-bottom: 18px; width: fit-content; position: relative; z-index: 2; }65 .tab-btn { padding: 7px 18px; border-radius: 5px; border: none; background: none; color: var(--text2); font-family: var(--sans); font-size: 13px; font-weight: 500; cursor: pointer; transition: all 0.15s; position: relative; z-index: 2; }66 .tab-btn.active { background: var(--bg4); color: var(--text); box-shadow: 0 1px 3px rgba(0,0,0,0.3); }67 .tab-content { display: none; position: relative; z-index: 1; }68 .tab-content.active { display: block; }69 70 .playground-layout { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 16px; }71 label.field-label { display: block; font-size: 11px; font-weight: 600; color: var(--text3); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 7px; }72 textarea, select, input[type="text"] { width: 100%; background: var(--bg3); border: 1px solid var(--border); border-radius: var(--radius-sm); color: var(--text); font-family: var(--mono); font-size: 12px; padding: 10px 12px; resize: vertical; outline: none; transition: border-color 0.15s, box-shadow 0.15s; line-height: 1.6; position: relative; z-index: 1; pointer-events: auto; cursor: text; }73 textarea:focus, select:focus, input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(79,156,249,0.15); }74 textarea:hover { border-color: var(--border2); }75 select { cursor: pointer; }76 select option { background: var(--bg3); }77 78 .btn { display: inline-flex; align-items: center; gap: 7px; padding: 9px 18px; border-radius: var(--radius-sm); font-family: var(--sans); font-size: 13px; font-weight: 500; cursor: pointer; border: none; transition: opacity 0.15s, transform 0.1s; }79 .btn:active { transform: scale(0.97); }80 .btn:disabled { opacity: 0.4; cursor: not-allowed; transform: none; }81 .btn-primary { background: var(--accent); color: #fff; }82 .btn-primary:hover:not(:disabled) { opacity: 0.88; }83 .btn-secondary { background: var(--bg4); color: var(--text); border: 1px solid var(--border2); }84 .btn-secondary:hover:not(:disabled) { background: var(--bg3); }85 .btn-ai { background: linear-gradient(135deg,#7c6af7,#4f9cf9); color: #fff; }86 .btn-ai:hover:not(:disabled) { opacity: 0.88; }87 .btn-success { background: rgba(61,214,140,0.15); color: var(--green); border: 1px solid rgba(61,214,140,0.25); }88 .btn-danger { background: rgba(240,82,82,0.1); color: var(--red); border: 1px solid rgba(240,82,82,0.2); }89 .btn-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }90 91 .result-panel { background: var(--bg3); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px; min-height: 60px; }92 .result-score { font-family: var(--mono); font-size: 36px; font-weight: 700; margin-bottom: 6px; }93 .score-high { color: var(--green); }94 .score-mid { color: var(--amber); }95 .score-low { color: var(--red); }96 .result-message { font-size: 13px; color: var(--text2); margin-bottom: 12px; }97 .breakdown-row { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; font-size: 12px; }98 .breakdown-label { color: var(--text3); width: 100px; font-family: var(--mono); }99 .breakdown-bar-wrap { flex: 1; background: var(--bg4); border-radius: 4px; height: 6px; overflow: hidden; }100 .breakdown-bar { height: 100%; border-radius: 4px; transition: width 0.4s ease; }101 .breakdown-val { color: var(--text2); font-family: var(--mono); width: 36px; text-align: right; font-size: 11px; }102 103 .code-block { background: var(--bg3); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 12px 14px; font-family: var(--mono); font-size: 11.5px; color: var(--text); white-space: pre-wrap; word-break: break-all; line-height: 1.65; max-height: 200px; overflow-y: auto; }104 105 table { width: 100%; border-collapse: collapse; font-size: 12px; }106 th { color: var(--text3); font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; font-weight: 600; padding: 8px 12px; border-bottom: 1px solid var(--border); text-align: left; }107 td { padding: 9px 12px; border-bottom: 1px solid var(--border); color: var(--text2); font-family: var(--mono); }108 tr:last-child td { border-bottom: none; }109 tr:hover td { background: var(--bg3); }110 111 .hint-box { background: rgba(245,166,35,0.07); border: 1px solid rgba(245,166,35,0.2); border-radius: var(--radius-sm); padding: 12px 14px; font-size: 12px; color: #e0b060; margin-top: 10px; display: none; }112 .hint-box.visible { display: block; }113 .hint-box strong { color: var(--amber); }114 115 .attempt-dots { display: flex; gap: 5px; align-items: center; }116 .attempt-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--bg4); border: 1px solid var(--border2); transition: background 0.2s; }117 .attempt-dot.used { background: var(--accent); border-color: var(--accent); }118 .attempt-dot.solved { background: var(--green); border-color: var(--green); }119 .attempt-dot.failed { background: var(--red); border-color: var(--red); }120 121 .status-badge { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; font-weight: 600; padding: 3px 9px; border-radius: 20px; font-family: var(--mono); }122 .status-solved { background: rgba(61,214,140,0.12); color: var(--green); }123 .status-partial { background: rgba(245,166,35,0.12); color: var(--amber); }124 .status-failed { background: rgba(240,82,82,0.1); color: var(--red); }125 .status-idle { background: var(--bg4); color: var(--text3); }126 .status-running { background: rgba(79,156,249,0.12); color: var(--accent); }127 128 @keyframes spin { to { transform: rotate(360deg); } }129 .spinner { width: 14px; height: 14px; border: 2px solid var(--border2); border-top-color: var(--accent); border-radius: 50%; animation: spin 0.7s linear infinite; display: none; }130 .spinner.active { display: inline-block; }131 132 .lb-entry { display: flex; align-items: center; gap: 12px; padding: 10px 14px; border-bottom: 1px solid var(--border); }133 .lb-entry:last-child { border-bottom: none; }134 .lb-rank { font-family: var(--mono); font-size: 13px; color: var(--text3); width: 24px; }135 .lb-model { font-family: var(--mono); font-size: 12px; color: var(--text); flex: 1; }136 .lb-score { font-family: var(--mono); font-size: 14px; font-weight: 700; }137 138 ::-webkit-scrollbar { width: 5px; height: 5px; }139 ::-webkit-scrollbar-track { background: transparent; }140 ::-webkit-scrollbar-thumb { background: var(--bg4); border-radius: 4px; }141 142 @keyframes fadeIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }143 .fade-in { animation: fadeIn 0.25s ease; }144 @keyframes slideIn { from { opacity: 0; transform: translateX(-8px); } to { opacity: 1; transform: none; } }145 .slide-in { animation: slideIn 0.3s ease forwards; }146 147 .divider { height: 1px; background: var(--border); margin: 16px 0; }148 .spacer { height: 1px; background: transparent; margin: 8px 0; }149 .tag { display: inline-block; font-size: 10px; font-family: var(--mono); padding: 2px 7px; border-radius: 4px; background: var(--bg4); color: var(--text3); border: 1px solid var(--border); }150 .info-row { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text2); padding: 6px 0; }151 .info-row .key { color: var(--text3); font-family: var(--mono); width: 130px; flex-shrink: 0; }152 153 /* AI Mode specific */154 .ai-controls { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 16px; }155 .agent-step { background: var(--bg3); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 14px; margin-bottom: 10px; opacity: 0; }156 .agent-step.visible { opacity: 1; }157 .agent-step-header { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }158 .step-num { font-family: var(--mono); font-size: 11px; font-weight: 700; color: var(--accent); background: rgba(79,156,249,0.12); padding: 2px 8px; border-radius: 20px; }159 .step-score { font-family: var(--mono); font-size: 12px; font-weight: 700; }160 .step-msg { font-size: 12px; color: var(--text2); margin-bottom: 8px; }161 .step-sql { font-family: var(--mono); font-size: 11px; color: var(--text); background: var(--bg4); border-radius: 4px; padding: 8px 10px; white-space: pre-wrap; word-break: break-all; max-height: 100px; overflow-y: auto; }162 163 .chart-wrap { position: relative; height: 200px; }164 165 .error-banner { background: rgba(240,82,82,0.1); border: 1px solid rgba(240,82,82,0.25); border-radius: var(--radius-sm); padding: 10px 14px; font-size: 12px; color: var(--red); margin-bottom: 12px; display: none; }166 .error-banner.visible { display: block; }167 168 .compare-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px,1fr)); gap: 12px; margin-top: 12px; }169 .compare-card { background: var(--bg3); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 14px; text-align: center; }170 .compare-model { font-family: var(--mono); font-size: 11px; color: var(--text2); margin-bottom: 6px; word-break: break-all; }171 .compare-score { font-family: var(--mono); font-size: 28px; font-weight: 700; margin-bottom: 4px; }172 .compare-meta { font-size: 11px; color: var(--text3); }173 </style>174 </head>175 <body>176 <div class="shell">177 <nav class="sidebar">178 <div class="logo">179 <div class="logo-title">SQL-DEBUG-ENV</div>180 <div class="logo-sub">OpenEnv Β· v2.0.0</div>181 </div>182 <div class="nav-section">183 <div class="nav-label">Overview</div>184 <button class="nav-item active" onclick="showPage('dashboard')">185 <span class="dot" style="background:var(--accent)"></span>Dashboard186 </button>187 <button class="nav-item" onclick="showPage('tasks')">188 <span class="dot" style="background:var(--text3)"></span>Tasks189 <span class="task-badge" style="background:var(--bg4);color:var(--text3)">5</span>190 </button>191 <button class="nav-item" onclick="showPage('leaderboard')">192 <span class="dot" style="background:var(--text3)"></span>Leaderboard193 </button>194 </div>195 <div class="nav-section" style="margin-top:8px">196 <div class="nav-label">Try it</div>197 <button class="nav-item" onclick="showPage('playground')">198 <span class="dot" style="background:var(--text3)"></span>Playground199 </button>200 <button class="nav-item" onclick="showPage('api')">201 <span class="dot" style="background:var(--text3)"></span>API Reference202 </button>203 </div>204 <div style="margin-top:auto;padding:16px 20px 0;border-top:1px solid var(--border)">205 <div style="font-size:11px;color:var(--text3);margin-bottom:8px">Environment status</div>206 <div style="display:flex;align-items:center;gap:7px;font-size:12px;color:var(--green)">207 <span style="width:7px;height:7px;border-radius:50%;background:var(--green);flex-shrink:0"></span>Running208 </div>209 </div>210 </nav>211 212 <main class="main">213 214 <!-- Dashboard -->215 <div id="page-dashboard" class="page active">216 <div class="page-header">217 <div>218 <div class="page-title">Dashboard</div>219 <div class="page-sub">SQL Query Debugging β OpenEnv benchmark environment</div>220 </div>221 <button class="btn btn-secondary" onclick="loadStats()">222 <span id="stats-spinner" class="spinner"></span>Refresh stats223 </button>224 </div>225 <div class="stats-grid">226 <div class="stat-card">227 <div class="stat-label">Total tasks</div>228 <div class="stat-value" id="stat-tasks">5</div>229 <div class="stat-sub">easy β expert</div>230 </div>231 <div class="stat-card">232 <div class="stat-label">Episodes run</div>233 <div class="stat-value" id="stat-episodes">β</div>234 <div class="stat-sub">this session</div>235 </div>236 <div class="stat-card">237 <div class="stat-label">Steps taken</div>238 <div class="stat-value" id="stat-steps">β</div>239 <div class="stat-sub">SQL submissions</div>240 </div>241 <div class="stat-card">242 <div class="stat-label">Uptime</div>243 <div class="stat-value" id="stat-uptime">β</div>244 <div class="stat-sub">seconds</div>245 </div>246 </div>247 <div style="display:grid;grid-template-columns:1fr 1fr;gap:16px">248 <div class="card">249 <div class="card-header"><div class="card-title">Task overview</div></div>250 <div id="task-overview-list"><div style="color:var(--text3);font-size:12px">Loadingβ¦</div></div>251 </div>252 <div class="card">253 <div class="card-header"><div class="card-title">Reward breakdown</div></div>254 <div style="font-size:12px;color:var(--text2);line-height:1.8;margin-bottom:12px">Partial credit at every step β agents get signal even for wrong answers.</div>255 <div class="breakdown-row"><div class="breakdown-label">syntax</div><div class="breakdown-bar-wrap"><div class="breakdown-bar" style="width:10%;background:var(--text3)"></div></div><div class="breakdown-val">0.10</div></div>256 <div class="breakdown-row"><div class="breakdown-label">execution</div><div class="breakdown-bar-wrap"><div class="breakdown-bar" style="width:20%;background:var(--accent)"></div></div><div class="breakdown-val">0.20</div></div>257 <div class="breakdown-row"><div class="breakdown-label">correctness</div><div class="breakdown-bar-wrap"><div class="breakdown-bar" style="width:70%;background:var(--green)"></div></div><div class="breakdown-val">0.70</div></div>258 <div class="breakdown-row"><div class="breakdown-label">optimization</div><div class="breakdown-bar-wrap"><div class="breakdown-bar" style="width:30%;background:var(--expert)"></div></div><div class="breakdown-val">+0.30</div></div>259 <div class="breakdown-row"><div class="breakdown-label">attempt penalty</div><div class="breakdown-bar-wrap"><div class="breakdown-bar" style="width:10%;background:var(--red)"></div></div><div class="breakdown-val">β0.02</div></div>260 </div>261 </div>262 <div class="card" style="margin-top:0">263 <div class="card-header">264 <div class="card-title">Baseline scores (llama-3.3-70b-versatile Β· Groq)</div>265 <span class="tag">temperature=0</span>266 </div>267 <table>268 <thead><tr><th>Task</th><th>Difficulty</th><th>Score</th><th>Attempts</th><th>Solved</th></tr></thead>269 <tbody>270 <tr><td>task_syntax_fix</td><td><span class="difficulty-badge diff-easy">easy</span></td><td><span style="color:var(--green);font-weight:600">0.9800</span></td><td>1</td><td><span class="status-badge status-solved">β yes</span></td></tr>271 <tr><td>task_logic_bug</td><td><span class="difficulty-badge diff-medium">medium</span></td><td><span style="color:var(--green);font-weight:600">0.9800</span></td><td>1</td><td><span class="status-badge status-solved">β yes</span></td></tr>272 <tr><td>task_optimization</td><td><span class="difficulty-badge diff-hard">hard</span></td><td><span style="color:var(--green);font-weight:600">1.0000</span></td><td>1</td><td><span class="status-badge status-solved">β yes</span></td></tr>273 <tr><td>task_injection_fix</td><td><span class="difficulty-badge diff-medium">medium</span></td><td><span style="color:var(--green);font-weight:600">1.0000</span></td><td>1</td><td><span class="status-badge status-solved">β yes</span></td></tr>274 <tr><td>task_reporting_rewrite</td><td><span class="difficulty-badge diff-expert">expert</span></td><td><span style="color:var(--green);font-weight:600">0.9800</span></td><td>1</td><td><span class="status-badge status-solved">β yes</span></td></tr>275 </tbody>276 </table>277 </div>278 </div>279 280 <!-- Tasks -->281 <div id="page-tasks" class="page">282 <div class="page-header">283 <div>284 <div class="page-title">Tasks</div>285 <div class="page-sub">5 tasks Β· easy β medium β hard β expert difficulty progression</div>286 </div>287 </div>288 <div class="task-grid" id="tasks-grid"></div>289 </div>290 291 <!-- Playground -->292 <div id="page-playground" class="page">293 <div class="page-header">294 <div>295 <div class="page-title">Playground</div>296 <div class="page-sub">Test SQL queries against any task interactively</div>297 </div>298 </div>299 300 <div class="card">301 <label class="field-label">Select task</label>302 <select id="pg-task-select" onchange="loadTask()">303 <option value="">β choose a task β</option>304 <option value="task_syntax_fix">task_syntax_fix Β· easy</option>305 <option value="task_logic_bug">task_logic_bug Β· medium</option>306 <option value="task_optimization">task_optimization Β· hard</option>307 <option value="task_injection_fix">task_injection_fix Β· medium</option>308 <option value="task_reporting_rewrite">task_reporting_rewrite Β· expert</option>309 </select>310 </div>311 312 <div id="pg-task-info" style="display:none">313 <div class="card">314 <div class="card-header">315 <div class="card-title" id="pg-task-title">β</div>316 <span id="pg-diff-badge" class="difficulty-badge">β</span>317 </div>318 <div style="font-size:13px;color:var(--text2);margin-bottom:12px" id="pg-task-desc">β</div>319 <label class="field-label">Broken query (what the agent sees)</label>320 <textarea 321 id="pg-broken-query" 322 rows="6"323 style="324 width:100%;325 background:#1a1e2a;326 color:#fff;327 border:1px solid #333;328 padding:12px;329 font-family:JetBrains Mono;330 font-size:12px;331 ">332 </textarea>333 </div>334 335 <!-- Tabs: Manual / AI -->336 <div class="tab-bar">337 <button class="tab-btn active" onclick="switchTab('manual')">βοΈ Manual Mode</button>338 <button class="tab-btn" onclick="switchTab('ai')">β‘ AI Mode</button>339 </div>340 341 <!-- Manual Tab -->342 <div id="tab-manual" class="tab-content active">343 <div class="playground-layout">344 <div class="card" style="margin-bottom:0; padding:16px;">345 <div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:8px;">346 <label class="field-label" style="margin-bottom:0">Your SQL fix</label>347 <span style="font-size:10px; color:var(--accent); font-family:var(--mono); background:rgba(79,156,249,0.1); padding:2px 8px; border-radius:4px;">β editable</span>348 </div>349 <textarea id="pg-sql-input" rows="10" placeholder="Write your corrected SQL here..." style="min-height:180px;"></textarea>350 </div>351 <div>352 <label class="field-label">Result</label>353 354 <div class="result-panel" id="pg-result">355 <div style="color:var(--text3);font-size:12px">356 Submit a query to see results357 </div>358 </div>359 360 <!-- π₯ ADD THIS BELOW RESULT -->361 <label class="field-label" style="margin-top:12px;">362 Expected vs Actual Diff363 </label>364 <div id="diff-table"></div>365 <div class="card" style="margin-top:12px;">366 <div class="card-header">367 <div class="card-title">Query Diff (Broken vs Your Fix)</div>368 </div>369 <div id="query-diff" style="font-family:JetBrains Mono;font-size:12px;"></div>370 </div>371 </div>372 </div>373 <div class="error-banner" id="manual-error"></div>374 <div class="btn-row">375 <button class="btn btn-primary" onclick="submitQuery()" id="pg-submit-btn">376 <span id="pg-spinner" class="spinner"></span>βΆ Run query377 </button>378 <button class="btn btn-secondary" onclick="startFreshEpisode()" title="Reset attempts counter and start a new episode">βΊ New episode</button>379 <button class="btn btn-secondary" onclick="resetEpisode()" title="Reload the broken query and reset everything">β³ Reload task</button>380 <div class="attempt-dots" id="pg-attempt-dots"></div>381 <span style="font-size:11px;color:var(--text3);font-family:var(--mono)" id="pg-attempt-label"></span>382 <span id="pg-status-badge" class="status-badge status-idle" style="margin-left:auto">idle</span>383 </div>384 <div class="hint-box" id="pg-hint-box"><strong>Hint:</strong> <span id="pg-hint-text"></span></div>385 <div class="hint-box" id="pg-hint-adv-box" style="border-color:rgba(176,108,245,0.3);background:rgba(176,108,245,0.07);color:#c090f0">386 <strong style="color:var(--expert)">Advanced hint:</strong> <span id="pg-hint-adv-text"></span>387 </div>388 </div>389 390 <!-- AI Tab -->391 <div id="tab-ai" class="tab-content">392 <div class="card">393 <div class="card-header"><div class="card-title">AI Agent Configuration</div></div>394 <div class="ai-controls">395 <div>396 <label class="field-label">Provider</label>397 <select id="ai-provider" onchange="updateModelList()">398 <option value="groq">Groq (Free)</option>399 <option value="openai">OpenAI</option>400 </select>401 </div>402 <div>403 <label class="field-label">Model</label>404 <select id="ai-model"></select>405 </div>406 </div>407 <div class="btn-row" style="margin-top:4px">408 <button class="btn btn-ai" onclick="runAgent()" id="ai-run-btn">409 <span id="ai-spinner" class="spinner"></span>β‘ Fix with AI410 </button>411 <button class="btn btn-secondary" onclick="runComparison()" id="ai-compare-btn">412 <span id="compare-spinner" class="spinner"></span>Compare All Models413 </button>414 <span id="ai-status-badge" class="status-badge status-idle" style="margin-left:auto">idle</span>415 </div>416 <div class="error-banner" id="ai-error"></div>417 </div>418 419 <div id="ai-compare-results" style="display:none">420 <div class="card">421 <div class="card-header"><div class="card-title">Model Comparison</div></div>422 <div class="compare-grid" id="compare-cards"></div>423 </div>424 </div>425 426 <div id="ai-reasoning-section" style="display:none">427 <div style="display:grid;grid-template-columns:1fr 1fr;gap:16px">428 <div class="card">429 <div class="card-header">430 <div class="card-title">AI Reasoning</div>431 <span id="ai-final-badge" class="status-badge status-idle">β</span>432 </div>433 <div id="agent-steps"></div>434 </div>435 <div class="card">436 <div class="card-header"><div class="card-title">Score Progression</div></div>437 <div class="chart-wrap">438 <canvas id="score-chart"></canvas>439 </div>440 <div style="margin-top:12px">441 <label class="field-label">Final AI Query</label>442 <div class="code-block" id="ai-final-query" style="max-height:150px">β</div>443 </div>444 </div>445 </div>446 </div>447 </div>448 </div>449 </div>450 451 <!-- Leaderboard -->452 <div id="page-leaderboard" class="page">453 <div class="page-header">454 <div>455 <div class="page-title">Leaderboard</div>456 <div class="page-sub">Best scores per task in this session</div>457 </div>458 <button class="btn btn-secondary" onclick="loadLeaderboard()">Refresh</button>459 </div>460 <div id="lb-content"><div style="color:var(--text3);font-size:13px;padding:20px 0">Loading leaderboardβ¦</div></div>461 </div>462 463 <!-- API Reference -->464 <div id="page-api" class="page">465 <div class="page-header">466 <div>467 <div class="page-title">API Reference</div>468 <div class="page-sub">All endpoints follow the OpenEnv spec</div>469 </div>470 </div>471 <div class="card">472 <div class="card-title" style="margin-bottom:14px">Base URL</div>473 <div class="code-block">https://Mahathi4554-sql-query-debugging.hf.space</div>474 </div>475 <div id="api-endpoints"></div>476 </div>477 478 </main>479 </div>480 481 <script>482 const BASE = "";483 let scoreChart = null;484 let pgSession = { attempt: 0, maxAttempts: 5, taskId: null, done: false };485 486 const PROVIDER_MODELS = {487 groq: ["llama-3.3-70b-versatile", "mixtral-8x7b-32768", "gemma2-9b-it"],488 openai: ["gpt-4o-mini", "gpt-4o", "gpt-3.5-turbo"],489 };490 491 // ββ Navigation ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ492 function showPage(id) {493 document.querySelectorAll(".page").forEach(p => p.classList.remove("active"));494 document.querySelectorAll(".nav-item").forEach(n => n.classList.remove("active"));495 document.getElementById("page-" + id).classList.add("active");496 const navEl = document.querySelector(`[onclick*="showPage('${id}')"]`);497 if (navEl) navEl.classList.add("active");498 if (id === "dashboard") loadStats();499 if (id === "tasks") loadTasks();500 if (id === "leaderboard") loadLeaderboard();501 if (id === "api") renderApiDocs();502 }503 504 // ββ Tabs βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ505 function switchTab(tab) {506 document.querySelectorAll(".tab-btn").forEach((b, i) => {507 b.classList.toggle("active", (i === 0 && tab === "manual") || (i === 1 && tab === "ai"));508 });509 document.getElementById("tab-manual").classList.toggle("active", tab === "manual");510 document.getElementById("tab-ai").classList.toggle("active", tab === "ai");511 }512 513 // ββ Provider / Model dropdowns ββββββββββββββββββββββββββββββββββββββββββ514 function updateModelList() {515 const provider = document.getElementById("ai-provider").value;516 const modelSel = document.getElementById("ai-model");517 modelSel.innerHTML = PROVIDER_MODELS[provider].map(m =>518 `<option value="${m}">${m}</option>`519 ).join("");520 }521 updateModelList();522 523 // ββ Stats ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ524 async function loadStats() {525 const sp = document.getElementById("stats-spinner");526 sp.classList.add("active");527 try {528 const r = await fetch(BASE + "/health");529 const d = await r.json();530 document.getElementById("stat-episodes").textContent = d.total_episodes ?? "β";531 document.getElementById("stat-steps").textContent = d.total_steps ?? "β";532 document.getElementById("stat-uptime").textContent = d.uptime_seconds ?? "β";533 } catch (e) {}534 try {535 const r = await fetch(BASE + "/stats");536 const d = await r.json();537 const list = document.getElementById("task-overview-list");538 list.innerHTML = "";539 for (const [tid, ts] of Object.entries(d.tasks || {})) {540 const el = document.createElement("div");541 el.className = "info-row";542 el.innerHTML = `543 <span class="key">${tid.replace("task_","")}</span>544 <span class="difficulty-badge diff-${ts.difficulty}" style="font-size:10px">${ts.difficulty}</span>545 <span style="margin-left:auto;font-family:var(--mono);font-size:11px;color:var(--text2)">546 ${ts.total_solves} solve${ts.total_solves!==1?"s":""}547 ${ts.best_score!==null?"Β· best "+ts.best_score.toFixed(4):""}548 </span>`;549 list.appendChild(el);550 }551 } catch (e) {552 document.getElementById("task-overview-list").innerHTML =553 '<div style="color:var(--text3);font-size:12px">Connect to a running server to see live stats.</div>';554 }555 sp.classList.remove("active");556 }557 558 // ββ Tasks ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ559 const TASK_DATA = [560 { id:"task_syntax_fix", diff:"easy", attempts:5, opt:false, desc:"Fix a single keyword typo (FORM β FROM) in an employee salary report query." },561 { id:"task_logic_bug", diff:"medium", attempts:7, opt:false, desc:"Fix 3 logic bugs: wrong JOIN type, wrong amount threshold, and reversed date comparison." },562 { id:"task_optimization", diff:"hard", attempts:7, opt:true, desc:"Rewrite correlated subqueries into an efficient JOIN + GROUP BY + HAVING approach." },563 { id:"task_injection_fix", diff:"medium", attempts:6, opt:false, desc:"Remove SQL injection payload (OR 1=1) and add correct active user filter." },564 { id:"task_reporting_rewrite", diff:"expert", attempts:8, opt:true, desc:"Replace LIKE pattern matching and OR chains with a CTE pre-aggregation + exact-match filter." },565 ];566 567 function loadTasks() {568 const grid = document.getElementById("tasks-grid");569 grid.innerHTML = "";570 TASK_DATA.forEach(t => {571 const card = document.createElement("div");572 card.className = "task-card fade-in";573 card.onclick = () => openTaskInPlayground(t.id);574 card.innerHTML = `575 <div class="task-card-header">576 <div class="task-name">${t.id}</div>577 <span class="difficulty-badge diff-${t.diff}">${t.diff}</span>578 </div>579 <div class="task-desc">${t.desc}</div>580 <div class="task-meta">581 <span>max ${t.attempts} attempts</span>582 ${t.opt?'<span style="color:var(--accent2)">+ optimization bonus</span>':""}583 </div>`;584 grid.appendChild(card);585 });586 }587 588 function openTaskInPlayground(taskId) {589 showPage("playground");590 document.getElementById("pg-task-select").value = taskId;591 loadTask();592 }593 594 // ββ Playground βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ595 async function loadTask() {596 const taskId = document.getElementById("pg-task-select").value;597 if (!taskId) { document.getElementById("pg-task-info").style.display = "none"; return; }598 document.getElementById("pg-task-info").style.display = "block";599 const task = TASK_DATA.find(t => t.id === taskId);600 if (task) {601 const diffMap = { easy:"diff-easy", medium:"diff-medium", hard:"diff-hard", expert:"diff-expert" };602 document.getElementById("pg-diff-badge").className = "difficulty-badge " + diffMap[task.diff];603 document.getElementById("pg-diff-badge").textContent = task.diff;604 document.getElementById("pg-task-title").textContent = task.id;605 }606 try {607 const r = await fetch(BASE + "/reset", {608 method: "POST", headers: {"Content-Type":"application/json"},609 body: JSON.stringify({ task_id: taskId })610 });611 const obs = await r.json();612 document.getElementById("pg-task-desc").textContent = obs.task_description;613 document.getElementById("pg-broken-query").value = obs.broken_query;614 document.getElementById("pg-sql-input").value = obs.broken_query.trim();615 pgSession = { attempt:0, maxAttempts:obs.max_attempts, taskId, done:false };616 renderAttemptDots();617 resetResultPanel();618 document.getElementById("pg-hint-box").classList.remove("visible");619 document.getElementById("pg-hint-adv-box").classList.remove("visible");620 updateStatusBadge("idle");621 hideError("manual-error");622 } catch (e) {623 document.getElementById("pg-task-desc").textContent = task?.desc || "";624 document.getElementById("pg-broken-query").value = "(Connect to a running server...)";625 }626 resetAISection();627 }628 629 async function resetEpisode() { await loadTask(); }630 631 async function startFreshEpisode() {632 try {633 const r = await fetch(BASE + "/reset", {634 method: "POST", headers: {"Content-Type":"application/json"},635 body: JSON.stringify({ task_id: pgSession.taskId })636 });637 const obs = await r.json();638 pgSession = { attempt:0, maxAttempts:obs.max_attempts, taskId:pgSession.taskId, done:false };639 renderAttemptDots();640 resetResultPanel();641 document.getElementById("pg-hint-box").classList.remove("visible");642 document.getElementById("pg-hint-adv-box").classList.remove("visible");643 updateStatusBadge("idle");644 hideEpisodeDoneBanner();645 hideError("manual-error");646 } catch(e) {647 showError("manual-error", "Could not reset episode: " + e.message);648 }649 }650 651 async function submitQuery() {652 const sql = document.getElementById("pg-sql-input").value.trim();653 if (!sql) return;654 655 // If max attempts hit, auto-start fresh episode before submitting656 if (pgSession.done) {657 await startFreshEpisode();658 }659 660 const btn = document.getElementById("pg-submit-btn");661 const sp = document.getElementById("pg-spinner");662 btn.disabled = true;663 sp.classList.add("active");664 hideError("manual-error");665 try {666 const r = await fetch(BASE + "/step", {667 method:"POST", headers:{"Content-Type":"application/json"},668 body: JSON.stringify({ sql_query: sql })669 });670 if (!r.ok) {671 const err = await r.json();672 showError("manual-error", err.detail || "Server error");673 btn.disabled = false;674 sp.classList.remove("active");675 return;676 }677 const result = await r.json();678 renderDiffTable(679 result.info.diff_data.expected,680 result.info.diff_data.actual681 );682 683 // π₯ ADD THIS684 diffSQL(685 document.getElementById("pg-broken-query").value,686 document.getElementById("pg-sql-input").value687 );688 const obs = result.observation;689 const reward = result.reward;690 pgSession.attempt = obs.attempt;691 pgSession.maxAttempts = obs.max_attempts;692 pgSession.done = result.done;693 renderAttemptDots();694 renderResult(reward);695 if (obs.hint) { document.getElementById("pg-hint-text").textContent = obs.hint; document.getElementById("pg-hint-box").classList.add("visible"); }696 if (obs.hint_advanced) { document.getElementById("pg-hint-adv-text").textContent = obs.hint_advanced; document.getElementById("pg-hint-adv-box").classList.add("visible"); }697 if (result.done) {698 const solved = reward.breakdown?.correctness >= 0.7;699 updateStatusBadge(solved ? "solved" : "failed");700 showEpisodeDoneBanner(solved, obs.attempt, obs.max_attempts);701 } else {702 updateStatusBadge("partial");703 hideEpisodeDoneBanner();704 }705 } catch (e) {706 showError("manual-error", e.message + ". Is the server running?");707 }708 btn.disabled = false;709 sp.classList.remove("active");710 }711 712 function showEpisodeDoneBanner(solved, attempt, max) {713 let el = document.getElementById("episode-done-banner");714 if (!el) {715 el = document.createElement("div");716 el.id = "episode-done-banner";717 el.style.cssText = "margin-top:12px;padding:10px 14px;border-radius:6px;font-size:12px;display:flex;align-items:center;justify-content:space-between;gap:12px;";718 const btnRow = document.querySelector("#tab-manual .btn-row");719 btnRow.parentNode.insertBefore(el, btnRow.nextSibling);720 }721 if (solved) {722 el.style.background = "rgba(61,214,140,0.08)";723 el.style.border = "1px solid rgba(61,214,140,0.25)";724 el.style.color = "#3dd68c";725 el.innerHTML = `<span>β Solved in ${attempt} attempt${attempt!==1?"s":""}! You can keep testing below β each new query starts a fresh episode automatically.</span><button class="btn btn-secondary" style="font-size:11px;padding:5px 12px;" onclick="startFreshEpisode()">βΊ Fresh start</button>`;726 } else {727 el.style.background = "rgba(240,82,82,0.07)";728 el.style.border = "1px solid rgba(240,82,82,0.2)";729 el.style.color = "#f05252";730 el.innerHTML = `<span>β Max ${max} attempts used. Edit your query and run again β it will auto-reset.</span><button class="btn btn-secondary" style="font-size:11px;padding:5px 12px;" onclick="startFreshEpisode()">βΊ Fresh start</button>`;731 }732 el.style.display = "flex";733 }734 735 function hideEpisodeDoneBanner() {736 const el = document.getElementById("episode-done-banner");737 if (el) el.style.display = "none";738 }739 740 function renderResult(reward) {741 const val = reward.value;742 const scoreClass = val >= 0.9 ? "score-high" : val >= 0.5 ? "score-mid" : "score-low";743 const bd = reward.breakdown || {};744 const barColor = k => {745 if (k==="correctness") return "var(--green)";746 if (k==="optimization") return "var(--expert)";747 if (k==="syntax") return "var(--text3)";748 return "var(--accent)";749 };750 const rows = Object.entries(bd).map(([k,v]) => `751 <div class="breakdown-row">752 <div class="breakdown-label">${k}</div>753 <div class="breakdown-bar-wrap"><div class="breakdown-bar" style="width:${Math.min(100,(v/1.0)*100)}%;background:${barColor(k)}"></div></div>754 <div class="breakdown-val">${v.toFixed(2)}</div>755 </div>`).join("");756 document.getElementById("pg-result").innerHTML = `757 <div class="result-score ${scoreClass}">${val.toFixed(4)}</div>758 <div class="result-message">${reward.message}</div>759 ${rows}`;760 }761 762 function resetResultPanel() {763 document.getElementById("pg-result").innerHTML =764 '<div style="color:var(--text3);font-size:12px">Submit a query to see results</div>';765 766 // clear diff table767 const diff = document.getElementById("diff-table");768 if (diff) diff.innerHTML = "";769 770 // π₯ ADD THIS (query diff reset)771 const qdiff = document.getElementById("query-diff");772 if (qdiff) qdiff.innerHTML = "";773 }774 775 function renderAttemptDots() {776 const wrap = document.getElementById("pg-attempt-dots");777 wrap.innerHTML = "";778 for (let i = 0; i < pgSession.maxAttempts; i++) {779 const d = document.createElement("div");780 d.className = "attempt-dot";781 if (i < pgSession.attempt) {782 const isLast = i === pgSession.attempt - 1;783 if (pgSession.done && isLast) {784 const solved = document.getElementById("pg-status-badge").textContent === "solved";785 d.classList.add(solved ? "solved" : "failed");786 } else {787 d.classList.add("used");788 }789 }790 wrap.appendChild(d);791 }792 const lbl = document.getElementById("pg-attempt-label");793 if (lbl) {794 lbl.textContent = pgSession.attempt > 0795 ? `${pgSession.attempt}/${pgSession.maxAttempts} attempts`796 : "";797 }798 }799 800 function updateStatusBadge(state) {801 const el = document.getElementById("pg-status-badge");802 const map = { idle:"status-idle", solved:"status-solved", partial:"status-partial", failed:"status-failed", running:"status-running" };803 el.className = "status-badge " + (map[state] || "status-idle");804 el.textContent = state;805 }806 807 // ββ AI Mode ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ808 function resetAISection() {809 document.getElementById("ai-reasoning-section").style.display = "none";810 document.getElementById("ai-compare-results").style.display = "none";811 document.getElementById("agent-steps").innerHTML = "";812 document.getElementById("ai-final-query").textContent = "β";813 document.getElementById("ai-final-badge").className = "status-badge status-idle";814 document.getElementById("ai-final-badge").textContent = "β";815 if (scoreChart) { scoreChart.destroy(); scoreChart = null; }816 hideError("ai-error");817 }818 819 async function runAgent() {820 const taskId = document.getElementById("pg-task-select").value;821 if (!taskId) { showError("ai-error","Please select a task first."); return; }822 const provider = document.getElementById("ai-provider").value;823 const model = document.getElementById("ai-model").value;824 const btn = document.getElementById("ai-run-btn");825 const sp = document.getElementById("ai-spinner");826 const userSQL = document.getElementById("pg-broken-query").value;827 btn.disabled = true;828 sp.classList.add("active");829 updateAIStatus("running");830 hideError("ai-error");831 resetAISection();832 833 try {834 const r = await fetch(BASE + "/baseline", {835 method:"POST", headers:{"Content-Type":"application/json"},836 837 body: JSON.stringify({838 provider,839 model,840 task_id: taskId,841 user_sql: userSQL // π₯ NEW842 })843 });844 if (!r.ok) {845 const err = await r.json();846 showError("ai-error", err.detail || "AI agent failed");847 updateAIStatus("failed");848 } else {849 const data = await r.json();850 // Find the task result851 const taskResult = data.tasks ? data.tasks.find(t => t.task_id === taskId) : data;852 if (taskResult) {853 await displayAgentTrace(taskResult);854 } else {855 showError("ai-error","No result returned for this task.");856 }857 }858 } catch (e) {859 showError("ai-error", e.message);860 updateAIStatus("failed");861 }862 btn.disabled = false;863 sp.classList.remove("active");864 }865 866 async function displayAgentTrace(result) {867 const steps = result.steps || [];868 const solved = result.solved || false;869 const finalScore = result.final_score || 0;870 871 document.getElementById("ai-reasoning-section").style.display = "block";872 873 // Update final badge874 const badge = document.getElementById("ai-final-badge");875 badge.className = "status-badge " + (solved ? "status-solved" : "status-failed");876 badge.textContent = solved ? `β solved ${finalScore.toFixed(4)}` : `β failed ${finalScore.toFixed(4)}`;877 878 // Show final query879 if (steps.length > 0) {880 document.getElementById("ai-final-query").textContent = steps[steps.length-1].query || "β";881 // π₯ ADD THIS (AI diff)882 diffSQL(883 document.getElementById("pg-broken-query").value,884 steps[steps.length - 1].query885 );886 }887 888 // Animate steps889 const stepsEl = document.getElementById("agent-steps");890 stepsEl.innerHTML = "";891 const scoreData = [];892 const labels = [];893 894 for (let i = 0; i < steps.length; i++) {895 await new Promise(r => setTimeout(r, 500));896 const s = steps[i];897 const scoreClass = s.score >= 0.9 ? "score-high" : s.score >= 0.5 ? "score-mid" : "score-low";898 const el = document.createElement("div");899 el.className = "agent-step slide-in";900 el.style.animationDelay = "0ms";901 el.innerHTML = `902 <div class="agent-step-header">903 <span class="step-num">Attempt ${s.attempt}</span>904 <span class="step-score ${scoreClass}">${s.score.toFixed(4)}</span>905 </div>906 <div class="step-msg">${s.message || ""}</div>907 <div class="step-sql">${escHtml(s.query || "")}</div>`;908 stepsEl.appendChild(el);909 setTimeout(() => el.classList.add("visible"), 50);910 911 scoreData.push(s.score);912 labels.push(`#${s.attempt}`);913 updateScoreChart(labels, scoreData);914 }915 916 updateAIStatus(solved ? "solved" : "failed");917 }918 919 function updateScoreChart(labels, data) {920 const ctx = document.getElementById("score-chart").getContext("2d");921 if (scoreChart) {922 scoreChart.data.labels = labels;923 scoreChart.data.datasets[0].data = data;924 scoreChart.update();925 return;926 }927 scoreChart = new Chart(ctx, {928 type: "line",929 data: {930 labels,931 datasets: [{932 label: "Score",933 data,934 borderColor: "#4f9cf9",935 backgroundColor: "rgba(79,156,249,0.1)",936 borderWidth: 2,937 pointBackgroundColor: data.map(v => v >= 0.9 ? "#3dd68c" : v >= 0.5 ? "#f5a623" : "#f05252"),938 pointRadius: 5,939 tension: 0.3,940 fill: true,941 }]942 },943 options: {944 responsive: true,945 maintainAspectRatio: false,946 plugins: { legend: { display: false } },947 scales: {948 y: { min: 0, max: 1.1, grid: { color: "rgba(255,255,255,0.05)" }, ticks: { color: "#555a6a", font: { family: "JetBrains Mono", size: 10 } } },949 x: { grid: { color: "rgba(255,255,255,0.05)" }, ticks: { color: "#555a6a", font: { family: "JetBrains Mono", size: 10 } } }950 }951 }952 });953 }954 955 function updateAIStatus(state) {956 const el = document.getElementById("ai-status-badge");957 const map = { idle:"status-idle", running:"status-running", solved:"status-solved", failed:"status-failed" };958 el.className = "status-badge " + (map[state] || "status-idle");959 el.textContent = state;960 }961 962 async function runComparison() {963 const taskId = document.getElementById("pg-task-select").value;964 if (!taskId) { showError("ai-error","Please select a task first."); return; }965 const provider = document.getElementById("ai-provider").value;966 const btn = document.getElementById("ai-compare-btn");967 const sp = document.getElementById("compare-spinner");968 btn.disabled = true;969 sp.classList.add("active");970 hideError("ai-error");971 resetAISection();972 973 try {974 const r = await fetch(BASE + "/baseline/compare", {975 method:"POST", headers:{"Content-Type":"application/json"},976 body: JSON.stringify({ provider, task_id: taskId })977 });978 if (!r.ok) {979 const err = await r.json();980 showError("ai-error", err.detail || "Comparison failed");981 } else {982 const data = await r.json();983 displayCompareResults(data.results || []);984 }985 } catch (e) {986 showError("ai-error", e.message);987 }988 btn.disabled = false;989 sp.classList.remove("active");990 }991 992 function displayCompareResults(results) {993 document.getElementById("ai-compare-results").style.display = "block";994 const grid = document.getElementById("compare-cards");995 const sorted = [...results].sort((a,b) => b.score - a.score);996 grid.innerHTML = sorted.map((r, i) => {997 const sc = r.score >= 0.9 ? "score-high" : r.score >= 0.5 ? "score-mid" : "score-low";998 return `999 <div class="compare-card fade-in">1000 ${i === 0 ? '<div style="font-size:18px;margin-bottom:4px">π₯</div>' : i === 1 ? '<div style="font-size:18px;margin-bottom:4px">π₯</div>' : i === 2 ? '<div style="font-size:18px;margin-bottom:4px">π₯</div>' : ''}1001 <div class="compare-model">${r.model}</div>1002 <div class="compare-score ${sc}">${r.score.toFixed(4)}</div>1003 <div class="compare-meta">${r.solved ? 'β solved' : 'β not solved'} Β· ${r.attempts} attempt${r.attempts!==1?'s':''}</div>1004 </div>`;1005 }).join("");1006 }1007 1008 // ββ Helpers βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ1009 function showError(id, msg) {1010 const el = document.getElementById(id);1011 if (el) { el.textContent = "β " + msg; el.classList.add("visible"); }1012 }1013 function renderDiffTable(expected, actual) {1014 const table = document.getElementById("diff-table");1015 if (!table) return;1016 1017 // π₯ Handle empty case1018 if (!expected || !actual || !expected.rows || !actual.rows) {1019 table.innerHTML = `<div style="color:var(--text3);font-size:12px">1020 No comparison data available1021 </div>`;1022 return;1023 }1024 1025 let html = `<table>1026 <thead>1027 <tr>1028 ${expected.columns.map(c => `<th>${c}</th>`).join("")}1029 </tr>1030 </thead>1031 <tbody>`;1032 1033 for (let i = 0; i < expected.rows.length; i++) {1034 html += "<tr>";1035 1036 for (let j = 0; j < expected.rows[i].length; j++) {1037 const exp = expected.rows[i][j];1038 const act = actual.rows?.[i]?.[j];1039 1040 const match = exp === act;1041 1042 html += `<td style="color:${match ? "#3dd68c" : "#f05252"}">1043 ${act ?? "NULL"}1044 </td>`;1045 }1046 1047 html += "</tr>";1048 }1049 1050 html += "</tbody></table>";1051 1052 table.innerHTML = html;1053 }1054 function diffSQL(oldSQL, newSQL) {1055 if (!oldSQL || !newSQL) return;1056 1057 const oldWords = oldSQL.split(/\s+/);1058 const newWords = newSQL.split(/\s+/);1059 1060 let result = "";1061 1062 newWords.forEach(word => {1063 if (!oldWords.includes(word)) {1064 result += `<span style="color:#3dd68c">${word}</span> `;1065 } else {1066 result += `<span style="color:#aaa;opacity:0.6">${word}</span> `;1067 }1068 });1069 1070 oldWords.forEach(word => {1071 if (!newWords.includes(word)) {1072 result += `<span style="color:#f05252;text-decoration:line-through">${word}</span> `;1073 }1074 });1075 1076 document.getElementById("query-diff").innerHTML = result;1077 }1078 function hideError(id) {1079 const el = document.getElementById(id);1080 if (el) { el.textContent = ""; el.classList.remove("visible"); }1081 }1082 function escHtml(s) {1083 return s.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");1084 }1085 1086 1087 // ββ Leaderboard βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ1088 async function loadLeaderboard() {1089 const container = document.getElementById("lb-content");1090 container.innerHTML = '<div style="color:var(--text3);font-size:12px">Loadingβ¦</div>';1091 try {1092 const r = await fetch(BASE + "/leaderboard");1093 const d = await r.json();1094 container.innerHTML = "";1095 for (const [tid, data] of Object.entries(d.leaderboard || {})) {1096 const card = document.createElement("div");1097 card.className = "card fade-in";1098 const entries = data.top_scores || [];1099 const rows = entries.length1100 ? entries.map((e,i) => `1101 <div class="lb-entry">1102 <div class="lb-rank">#${i+1}</div>1103 <div class="lb-model">${e.model || "anonymous"}</div>1104 <div style="font-size:11px;color:var(--text3);font-family:var(--mono);flex:1">${e.attempts?"attempts: "+e.attempts:""}</div>1105 <div class="lb-score ${e.score>=0.9?"score-high":e.score>=0.5?"score-mid":"score-low"}">${e.score.toFixed(4)}</div>1106 </div>`).join("")1107 : '<div style="color:var(--text3);font-size:12px;padding:10px 14px">No entries yet. Run the playground or baseline to populate.</div>';1108 card.innerHTML = `1109 <div class="card-header">1110 <div class="card-title">${tid}</div>1111 <div style="display:flex;gap:8px;align-items:center">1112 <span class="difficulty-badge diff-${data.difficulty}">${data.difficulty}</span>1113 <span class="tag">${data.total_attempts} attempts</span>1114 </div>1115 </div>1116 ${rows}`;1117 container.appendChild(card);1118 }1119 } catch (e) {1120 container.innerHTML = '<div style="color:var(--text3);font-size:12px;padding:8px 0">Connect to a running server to see the leaderboard.</div>';1121 }1122 }1123 1124 // ββ API Docs ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ1125 function renderApiDocs() {1126 const endpoints = [1127 { method:"GET", path:"/health", desc:"Environment status, version, uptime, episode count" },1128 { method:"POST", path:"/reset", desc:'Start episode. Body: {"task_id":"task_syntax_fix"} (optional)', body:'{"task_id": "task_syntax_fix"}' },1129 { method:"POST", path:"/step", desc:'Submit SQL query. Body: {"sql_query":"SELECT ..."}', body:'{"sql_query": "SELECT name FROM employees;"}' },1130 { method:"GET", path:"/state", desc:"Current episode state (no side effects)" },1131 { method:"GET", path:"/tasks", desc:"List all 5 tasks with schema, difficulty, action spec" },1132 { method:"POST", path:"/grader", desc:'Grade standalone. Body: {"task_id":"...","sql_query":"..."}', body:'{"task_id":"task_syntax_fix","sql_query":"SELECT name, salary FROM employees WHERE department=\'Engineering\' ORDER BY salary DESC;"}' },1133 { method:"POST", path:"/baseline", desc:'Run AI agent. Body: {"provider":"groq","model":"llama-3.3-70b-versatile","task_id":"..."}', body:'{"provider":"groq","model":"llama-3.3-70b-versatile","task_id":"task_syntax_fix"}' },1134 { method:"POST", path:"/baseline/compare", desc:'Compare all models. Body: {"provider":"groq","task_id":"..."}', body:'{"provider":"groq","task_id":"task_syntax_fix"}' },1135 { method:"GET", path:"/leaderboard", desc:"Best scores per task this session" },1136 { method:"GET", path:"/stats", desc:"Aggregate usage statistics" },1137 ];1138 const colorMap = { GET:"var(--green)", POST:"var(--accent)", PUT:"var(--amber)", DELETE:"var(--red)" };1139 const container = document.getElementById("api-endpoints");1140 container.innerHTML = "";1141 endpoints.forEach(ep => {1142 const card = document.createElement("div");1143 card.className = "card";1144 card.innerHTML = `1145 <div style="display:flex;align-items:center;gap:10px;margin-bottom:8px">1146 <span style="font-family:var(--mono);font-size:11px;font-weight:700;color:${colorMap[ep.method]};background:${colorMap[ep.method]}20;padding:3px 8px;border-radius:4px;min-width:42px;text-align:center">${ep.method}</span>1147 <span style="font-family:var(--mono);font-size:13px;font-weight:600;color:var(--text)">${ep.path}</span>1148 </div>1149 <div style="font-size:12px;color:var(--text2);margin-bottom:${ep.body?"10px":"0"}">${ep.desc}</div>1150 ${ep.body?`<label class="field-label" style="margin-top:8px">Example body</label><div class="code-block">${ep.body}</div>`:""}`;1151 container.appendChild(card);1152 });1153 }1154 1155 // ββ Init ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ1156 loadStats();1157 // Sync broken query input1158 document.addEventListener("input", function(e) {1159 if (e.target.id === "pg-broken-query") {1160 const sqlInput = document.getElementById("pg-sql-input");1161 if (sqlInput) {1162 sqlInput.value = e.target.value;1163 }1164 }1165 });1166 </script>1167 </body>1168</html>