Team Ai
Apppublic

Mahathi4554/sql-query-debugging

sourceHugging Faceupdated 7mo agoView on Hugging Face
0likes
Dashboard.html1168 linesDownload Raw Back to root
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,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");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>