NohTow/BrowseComp-Plus
0
1const TABLE_BODY = document.getElementById('table-body');2const TABLE_HEADER = document.getElementById('table-header');3const SEARCH_INPUT = document.getElementById('model-search');4const STATS_SUMMARY = document.getElementById('stats-summary');5const TOP_SCROLLBAR_WRAPPER = document.getElementById('top-scrollbar-wrapper');6const TOP_SCROLLBAR_CONTENT = document.getElementById('top-scrollbar-content');7const BOTTOM_SCROLLBAR_WRAPPER = document.getElementById('bottom-scrollbar-wrapper');8const TABLE = document.getElementById('leaderboard-table');9 10let rawData = [];11let displayData = [];12let sortKey = 'Accuracy (%)';13let sortAsc = false;14 15// Metrics we want to display as columns (from left to right)16const COLUMNS = [17 { id: 'LLM', label: 'LLM', class: 'col-llm' },18 { id: 'Retriever', label: 'Retriever', class: 'col-retriever' },19 { id: 'Submitted By', label: 'Submitted By', class: 'col-submitted' },20 { id: 'Scaffold', label: 'Scaffold', class: 'col-scaffold' },21 { id: 'Accuracy (%)', label: 'Accuracy (%)', class: 'col-metric' },22 { id: 'Recall (%)', label: 'Recall (%)', class: 'col-metric' },23 { id: 'Search Calls', label: 'Search Calls', class: 'col-metric' },24 { id: 'Calibration Error (%)', label: 'Calibration Error (%)', class: 'col-metric' },25];26 27async function init() {28 try {29 // Cache buster to ensure fresh data from the extraction script30 const v = Date.now();31 const response = await fetch(`data/leaderboard.json?v=${v}`);32 33 if (response.ok) {34 rawData = await response.json();35 } else {36 console.error('Leaderboard JSON not found.');37 }38 39 renderHeaders();40 updateDisplayData();41 renderTable();42 initScrollSync();43 await initMarkdown();44 await renderCitation();45 46 SEARCH_INPUT.addEventListener('input', e => {47 updateDisplayData(e.target.value);48 renderTable();49 updateScrollWidth();50 });51 52 window.addEventListener('resize', updateScrollWidth);53 updateScrollWidth();54 55 } catch (err) {56 console.error('Failed to load data:', err);57 TABLE_BODY.innerHTML = '<tr><td colspan="100%" style="color: red; text-align: center;">Error loading benchmark data.</td></tr>';58 }59}60 61async function initMarkdown() {62 const introEl = document.getElementById('intro-markdown');63 const metricsEl = document.getElementById('metrics-details');64 const submittingEl = document.getElementById('submit-here');65 66 try {67 const [introRes, metricsRes, submittingRes] = await Promise.all([68 fetch('blobs/intro.md'),69 fetch('blobs/metrics.md'),70 fetch('blobs/submitting.md')71 ]);72 73 if (introRes.ok) introEl.innerHTML = marked.parse(await introRes.text());74 if (metricsRes.ok) metricsEl.innerHTML = marked.parse(await metricsRes.text());75 if (submittingRes.ok) submittingEl.innerHTML = marked.parse(await submittingRes.text());76 77 // Expand/Collapse logic78 document.querySelectorAll('.expand-btn').forEach(btn => {79 btn.addEventListener('click', () => {80 const targetId = btn.getAttribute('data-target');81 if (!targetId) return; // Skip external links82 83 const targetEl = document.getElementById(targetId);84 const isActive = btn.classList.contains('active');85 86 if (isActive) {87 btn.classList.remove('active');88 targetEl.classList.remove('active');89 } else {90 // Optional: close other expandable sections91 document.querySelectorAll('.expand-btn').forEach(b => b.classList.remove('active'));92 document.querySelectorAll('.expandable-content').forEach(c => c.classList.remove('active'));93 94 btn.classList.add('active');95 targetEl.classList.add('active');96 97 // Smooth scroll to content98 setTimeout(() => {99 targetEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' });100 }, 100);101 }102 });103 });104 105 } catch (err) {106 console.error('Failed to load markdown blobs:', err);107 }108}109 110function initScrollSync() {111 TOP_SCROLLBAR_WRAPPER.addEventListener('scroll', () => {112 BOTTOM_SCROLLBAR_WRAPPER.scrollLeft = TOP_SCROLLBAR_WRAPPER.scrollLeft;113 });114 BOTTOM_SCROLLBAR_WRAPPER.addEventListener('scroll', () => {115 TOP_SCROLLBAR_WRAPPER.scrollLeft = BOTTOM_SCROLLBAR_WRAPPER.scrollLeft;116 });117}118 119function updateScrollWidth() {120 TOP_SCROLLBAR_CONTENT.style.width = TABLE.offsetWidth + 'px';121}122 123function parseCSV(csv) {124 const lines = csv.split('\n').filter(line => line.trim() !== '');125 if (lines.length === 0) return [];126 127 const headers = parseCSVLine(lines[0]);128 return lines.slice(1).map(line => {129 const values = parseCSVLine(line);130 const obj = {};131 headers.forEach((header, i) => {132 obj[header.trim()] = values[i] ? values[i].trim() : '';133 });134 return obj;135 });136}137 138function parseCSVLine(line) {139 const result = [];140 let cur = '';141 let inQuotes = false;142 for (let i = 0; i < line.length; i++) {143 const char = line[i];144 if (char === '"') {145 inQuotes = !inQuotes;146 } else if (char === ',' && !inQuotes) {147 result.push(cur);148 cur = '';149 } else {150 cur += char;151 }152 }153 result.push(cur);154 return result;155}156 157function renderHeaders() {158 let html = `<th style="width: 60px;">#</th>`;159 160 COLUMNS.forEach(col => {161 const arrow = sortKey === col.id ? (sortAsc ? ' \u2191' : ' \u2193') : '';162 html += `<th onclick="handleSort('${col.id}')" class="${col.class || ''}">${col.label}${arrow}</th>`;163 });164 165 TABLE_HEADER.innerHTML = html;166}167 168function handleSort(key) {169 if (sortKey === key) {170 sortAsc = !sortAsc;171 } else {172 sortKey = key;173 sortAsc = false;174 }175 176 renderHeaders();177 updateDisplayData(SEARCH_INPUT.value);178 renderTable();179}180 181window.handleSort = handleSort;182 183function updateDisplayData(query = '') {184 displayData = rawData.filter(item => {185 const searchStr = `${item.LLM} ${item.Retriever} ${item["Submitted By"]}`.toLowerCase();186 return searchStr.includes(query.toLowerCase());187 });188 189 displayData.sort((a, b) => {190 let valA = a[sortKey];191 let valB = b[sortKey];192 193 if (['Accuracy (%)', 'Recall (%)', 'Search Calls', 'Calibration Error (%)'].includes(sortKey)) {194 valA = parseFloat(valA) || 0;195 valB = parseFloat(valB) || 0;196 } else {197 if (typeof valA === 'string') valA = valA.toLowerCase();198 if (typeof valB === 'string') valB = valB.toLowerCase();199 }200 201 if (valA < valB) return sortAsc ? -1 : 1;202 if (valA > valB) return sortAsc ? 1 : -1;203 return 0;204 });205 206 STATS_SUMMARY.innerHTML = `Showing <strong>${displayData.length}</strong> results`;207}208 209function formatScaffold(scaffold) {210 if (!scaffold || scaffold.trim() === '') {211 const text = 'The default scaffold in BrowseComp-Plus, with a `search` function that returns top 5 docs, truncated by 512 tokens.';212 return {213 display: 'Standard',214 title: 'Standard',215 text: text.replace(/`([^`]+)`/g, '<code class="tooltip-code">$1</code>')216 };217 }218 219 const parts = scaffold.split(':');220 const title = parts[0].trim();221 let text = parts.length > 1 ? parts.slice(1).join(':').trim() : '';222 223 // Highlight terms bracketed in backticks224 text = text.replace(/`([^`]+)`/g, '<code class="tooltip-code">$1</code>');225 226 return {227 display: 'Custom',228 title: title,229 text: text230 };231}232 233function renderTable() {234 if (displayData.length === 0) {235 TABLE_BODY.innerHTML = '<tr><td colspan="100%" style="text-align: center;">No matching models found.</td></tr>';236 return;237 }238 239 TABLE_BODY.innerHTML = displayData.map((row, index) => {240 const scaffoldInfo = formatScaffold(row.Scaffold);241 const isOpenWeight = row["Open Weights?"] === 'Yes';242 const modelSize = row["Model Size (B)"];243 244 return `245 <tr>246 <!-- Rank & Weight Column -->247 <td style="text-align: center; vertical-align: top; padding-top: 1.5rem;">248 <div style="display: flex; flex-direction: column; align-items: center; gap: 6px;">249 <span style="color: var(--text-secondary); font-size: 0.8rem; font-weight: 600;">${index + 1}</span>250 ${isOpenWeight ?251 `<span class="tag-open-weight">${modelSize}B</span>` :252 `<span class="tag-proprietary">Proprietary</span>`253 }254 </div>255 </td>256 257 <!-- LLM Column -->258 <td class="col-llm">259 <div style="display: flex; flex-direction: column;">260 <a href="${row["LLM Link"] || '#'}" target="_blank" class="model-name">${row.LLM}</a>261 <div class="evaluation-date">262 ${row["Evaluation Date"] || 'N/A'}263 </div>264 </div>265 </td>266 267 <!-- Retriever Column -->268 <td class="col-retriever">269 <a href="${row["Retriever Link"] || '#'}" target="_blank" style="color: var(--text-primary); text-decoration: none; font-weight: 500;">270 ${row.Retriever}271 </a>272 </td>273 274 <!-- Submitted By Column -->275 <td class="col-submitted">276 ${(() => {277 const isInternal = row["Submitted By"] === 'BrowseComp-Plus';278 const style = isInternal ?279 'color: var(--text-secondary); font-size: 0.9rem;' :280 'color: var(--accent-secondary); font-size: 0.9rem; font-weight: 600;';281 const border = isInternal ? 'border-bottom: 1px dashed #cbd5e1;' : 'border-bottom: 1px dashed rgba(124, 58, 237, 0.3);';282 283 return row["Submittor Link"] ?284 `<a href="${row["Submittor Link"]}" target="_blank" style="${style} ${border} text-decoration: none;">${row["Submitted By"]}</a>` :285 `<span style="${style}">${row["Submitted By"]}</span>`;286 })()}287 </td>288 289 <!-- Scaffold Column -->290 <td class="col-scaffold">291 <div class="tooltip-container">292 <span class="tag-scaffold ${scaffoldInfo.display === 'Custom' ? 'tag-scaffold-custom' : 'tag-scaffold-standard'}">293 ${scaffoldInfo.display}294 </span>295 <div class="tooltip-content">296 <div class="tooltip-title">${scaffoldInfo.title}</div>297 <div class="tooltip-text">${scaffoldInfo.text}</div>298 </div>299 </div>300 </td>301 302 <!-- Accuracy (%) -->303 <td class="metric-cell col-metric">${parseFloat(row["Accuracy (%)"]).toFixed(2)}%</td>304 305 <!-- Recall (%) -->306 <td class="col-metric" style="font-weight: 500; color: var(--text-primary);">${parseFloat(row["Recall (%)"]).toFixed(2)}%</td>307 308 <!-- Search Calls -->309 <td class="col-metric" style="color: var(--text-primary);">${parseFloat(row["Search Calls"]).toFixed(2)}</td>310 311 <!-- Calibration Error (%) -->312 <td class="col-metric" style="color: var(--text-primary);">313 ${row["Calibration Error (%)"] && row["Calibration Error (%)"] !== '-' ? parseFloat(row["Calibration Error (%)"]).toFixed(2) + '%' : 'N/A'}314 </td>315 </tr>316 `}).join('');317}318 319async function renderCitation() {320 const citationEl = document.getElementById('citation-content');321 const copyBtn = document.getElementById('copy-citation-btn');322 323 try {324 const response = await fetch('blobs/citation.md');325 if (response.ok) {326 const text = await response.text();327 citationEl.textContent = text;328 329 copyBtn.addEventListener('click', () => {330 navigator.clipboard.writeText(text).then(() => {331 const originalHtml = copyBtn.innerHTML;332 copyBtn.innerHTML = '<i class="fa-solid fa-check"></i> Copied!';333 copyBtn.classList.add('copied');334 335 setTimeout(() => {336 copyBtn.innerHTML = originalHtml;337 copyBtn.classList.remove('copied');338 }, 2000);339 });340 });341 }342 } catch (err) {343 console.error('Failed to load citation:', err);344 }345}346 347init();348 