Skip to content

Commit 292dff9

Browse files
committed
feat: update leaderboard layout
1 parent 480300a commit 292dff9

2 files changed

Lines changed: 250 additions & 102 deletions

File tree

community.html

Lines changed: 235 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,36 @@ <h1 class="text-4xl font-bold bg-gradient-to-r from-blue-400 to-purple-400 bg-cl
5454
</p>
5555
</div>
5656

57-
<!-- Community Submissions -->
58-
<div id="community-submissions" class="grid gap-6">
57+
<!-- Community Leaderboard -->
58+
<div class="bg-gray-800 rounded-lg border border-gray-700 mb-6 sm:mb-8">
59+
<div class="w-full overflow-x-auto table-container">
60+
<table class="w-full table-auto rounded-lg overflow-hidden">
61+
<thead class="bg-gray-700">
62+
<tr>
63+
<th class="px-2 py-3 text-center text-xs sm:text-sm font-medium text-gray-300 w-12 sm:w-16">Rank</th>
64+
<th class="px-2 py-3 text-left text-xs sm:text-sm font-medium text-gray-300 min-w-32 max-w-48">Model</th>
65+
<th class="px-2 py-3 text-left text-xs sm:text-sm font-medium text-gray-300 w-20 sm:w-24">Provider</th>
66+
<th class="px-2 py-3 text-left text-xs sm:text-sm font-medium text-gray-300 w-20 sm:w-24">Avg Final Round</th>
67+
<th class="px-2 py-3 text-left text-xs sm:text-sm font-medium text-gray-300 w-16 sm:w-20 hidden md:table-cell">Ante</th>
68+
<th class="px-2 py-3 text-left text-xs sm:text-sm font-medium text-gray-300 w-16 sm:w-20 hidden md:table-cell">Win %</th>
69+
<th class="px-2 py-3 text-left text-xs sm:text-sm font-medium text-gray-300 w-20 sm:w-24 hidden lg:table-cell">Tool Call</th>
70+
<th class="px-2 py-3 text-left text-xs sm:text-sm font-medium text-gray-300 w-20 sm:w-24 hidden lg:table-cell">Tool Call Errors</th>
71+
<th class="px-2 py-3 text-left text-xs sm:text-sm font-medium text-gray-300 w-20 sm:w-24 hidden xl:table-cell">Token (Total)</th>
72+
<th class="px-2 py-3 text-left text-xs sm:text-sm font-medium text-gray-300 w-20 sm:w-24 hidden xl:table-cell">Input Token</th>
73+
<th class="px-2 py-3 text-left text-xs sm:text-sm font-medium text-gray-300 w-20 sm:w-24 hidden xl:table-cell">Output Token</th>
74+
<th class="px-2 py-3 text-left text-xs sm:text-sm font-medium text-gray-300 w-16 sm:w-20 hidden lg:table-cell">Avg Time</th>
75+
<th class="px-2 py-3 text-left text-xs sm:text-sm font-medium text-gray-300 w-16 sm:w-20">Cost</th>
76+
</tr>
77+
</thead>
78+
<tbody id="community-leaderboard-body" class="divide-y divide-gray-700">
79+
<!-- Data will be loaded here by JavaScript -->
80+
</tbody>
81+
</table>
82+
</div>
83+
</div>
84+
85+
<!-- Community Submissions (Legacy) -->
86+
<div id="community-submissions" class="grid gap-6 hidden">
5987
<!-- Data will be loaded here by JavaScript -->
6088
</div>
6189

@@ -114,89 +142,224 @@ <h4 class="font-semibold text-white mb-2">3. Submit PR</h4>
114142
</div>
115143

116144
<script>
117-
// Load community submissions
118-
async function loadCommunitySubmissions() {
145+
// Load community leaderboard data
146+
async function loadCommunityLeaderboard() {
119147
try {
120-
// Fetch official leaderboard data
121-
const officialResponse = await fetch('data/benchmarks/v0.2.0/default/leaderboard.json');
122-
const officialData = await officialResponse.json();
123-
const officialSubmissions = officialData.entries.map(entry => ({
124-
title: entry.model,
125-
author: 'Official Benchmark',
126-
model: entry.model,
127-
score: entry.performance_score.toFixed(1),
128-
votes: entry.total_runs,
129-
date: officialData.generated_at,
130-
description: `Official benchmark run with ${entry.total_runs} runs. Reached ante ${entry.avg_final_ante} on average.`
131-
}));
132-
133-
// Fetch community submissions
134-
// In a real application, you would dynamically list files in the directory.
135-
// For this static site, we list them manually.
136-
const communityFiles = ['data/strategies/default/community-submission-1.json'];
137-
const communitySubmissions = [];
138-
for (const file of communityFiles) {
139-
try {
140-
const response = await fetch(file);
141-
if (response.ok) {
142-
const submission = await response.json();
143-
communitySubmissions.push(submission);
144-
}
145-
} catch (error) {
146-
console.warn(`Could not load community submission: ${file}`, error);
148+
// Discover community runs
149+
const communityRuns = await discoverCommunityRuns();
150+
151+
if (communityRuns.length === 0) {
152+
showError('No community runs found');
153+
return;
154+
}
155+
156+
// Sort runs by ante reached (descending), then by score (descending)
157+
communityRuns.sort((a, b) => {
158+
if (b.stats.ante_reached !== a.stats.ante_reached) {
159+
return b.stats.ante_reached - a.stats.ante_reached;
160+
}
161+
return b.stats.final_score - a.stats.final_score;
162+
});
163+
164+
// Add rank
165+
communityRuns.forEach((run, index) => {
166+
run.rank = index + 1;
167+
});
168+
169+
renderCommunityLeaderboard(communityRuns);
170+
171+
} catch (error) {
172+
console.error('Error loading community leaderboard:', error);
173+
showError('Error loading community data');
174+
}
175+
}
176+
177+
// Discover community runs from the data/community directory
178+
async function discoverCommunityRuns() {
179+
// Known run directories (in a real app, this would be dynamic)
180+
const knownRuns = [
181+
'20250826_180521_RedDeck_s1_OOOO155',
182+
'20250826_185432_RedDeck_s1_OOOO155'
183+
];
184+
185+
const communityRuns = [];
186+
187+
for (const runDir of knownRuns) {
188+
try {
189+
const configResponse = await fetch(`data/community/${runDir}/config.json`);
190+
const statsResponse = await fetch(`data/community/${runDir}/stats.json`);
191+
192+
if (configResponse.ok && statsResponse.ok) {
193+
const config = await configResponse.json();
194+
const stats = await statsResponse.json();
195+
196+
communityRuns.push({
197+
runDir,
198+
config,
199+
stats,
200+
timestamp: runDir.split('_')[1] // Extract timestamp from directory name
201+
});
147202
}
203+
} catch (error) {
204+
console.warn(`Failed to load run: ${runDir}`, error);
148205
}
206+
}
207+
208+
return communityRuns;
209+
}
149210

150-
// Combine and sort
151-
const allSubmissions = [...officialSubmissions, ...communitySubmissions];
152-
allSubmissions.sort((a, b) => parseFloat(b.score) - parseFloat(a.score));
153-
154-
const container = document.getElementById('community-submissions');
155-
container.innerHTML = allSubmissions.map(submission => `
156-
<div class="bg-gray-800 rounded-lg border border-gray-700 shadow-lg hover:shadow-xl transition-all duration-300">
157-
<div class="p-6">
158-
<div class="flex items-start justify-between mb-4">
159-
<div>
160-
<h3 class="text-xl font-bold text-white mb-2">${submission.title}</h3>
161-
<div class="flex items-center gap-4 text-sm text-gray-400">
162-
<span>by ${submission.author}</span>
163-
<span class="bg-gray-700 px-2 py-1 rounded text-xs">${submission.model}</span>
164-
<span>${new Date(submission.date).toLocaleDateString()}</span>
165-
</div>
166-
</div>
167-
<div class="text-right">
168-
<div class="text-2xl font-bold bg-gradient-to-r from-blue-400 to-purple-400 bg-clip-text text-transparent">
169-
${submission.score} Ante
170-
</div>
171-
<div class="text-sm text-gray-400">
172-
${submission.votes} votes
211+
// Render community leaderboard
212+
function renderCommunityLeaderboard(runs) {
213+
const tbody = document.getElementById('community-leaderboard-body');
214+
if (!tbody) return;
215+
216+
tbody.innerHTML = runs.map((run, index) => {
217+
const modelParts = run.config.model.split('/');
218+
const provider = modelParts[0] || '';
219+
const modelName = modelParts.slice(1).join('/') || run.config.model;
220+
221+
return `
222+
<tr class="hover:bg-gray-700 transition-colors cursor-pointer" onclick="toggleCommunityStats(${index})">
223+
<td class="px-2 py-3 text-center">
224+
<span class="text-xs sm:text-sm font-bold ${getRankColor(run.rank)}">#${run.rank}</span>
225+
</td>
226+
<td class="px-2 py-3 min-w-32 max-w-48">
227+
<div class="overflow-hidden">
228+
<div class="font-medium text-white text-xs sm:text-sm break-words" title="${modelName}">${modelName}</div>
229+
<div class="text-xs text-gray-400 mt-0.5">
230+
<span class="md:hidden">${run.stats.ante_reached} ante • ${run.stats.run_won ? 'Win' : 'Loss'}</span>
231+
<span class="hidden md:inline">${run.config.deck}${run.config.seed}</span>
232+
</div>
233+
</div>
234+
</td>
235+
<td class="px-2 py-3">
236+
<div class="text-xs sm:text-sm font-medium text-gray-300 capitalize">${provider}</div>
237+
</td>
238+
<td class="px-2 py-3">
239+
<div class="text-xs sm:text-sm font-bold text-white">${run.stats.final_round || 'N/A'}</div>
240+
<div class="text-xs text-gray-400">final round</div>
241+
</td>
242+
<td class="px-2 py-3 hidden md:table-cell">
243+
<div class="text-xs sm:text-sm font-bold text-white">${run.stats.ante_reached}</div>
244+
<div class="text-xs text-gray-400">${run.stats.run_won ? 'Win' : 'Loss'}</div>
245+
</td>
246+
<td class="px-2 py-3 hidden md:table-cell">
247+
<div class="font-medium text-white text-xs sm:text-sm">${run.stats.run_won ? '100' : '0'}%</div>
248+
<div class="text-xs text-gray-400">1 run</div>
249+
</td>
250+
<td class="px-2 py-3 hidden lg:table-cell">
251+
<div class="font-medium text-white text-xs sm:text-sm">${run.stats.tool_calls || 'N/A'}</div>
252+
<div class="text-xs text-gray-400">calls</div>
253+
</td>
254+
<td class="px-2 py-3 hidden lg:table-cell">
255+
<div class="font-medium text-white text-xs sm:text-sm">${run.stats.tool_call_errors || 0}</div>
256+
<div class="text-xs text-gray-400">errors</div>
257+
</td>
258+
<td class="px-2 py-3 hidden xl:table-cell">
259+
<div class="font-medium text-white text-xs sm:text-sm">${run.stats.total_tokens ? (run.stats.total_tokens / 1000).toFixed(0) + 'k' : 'N/A'}</div>
260+
<div class="text-xs text-gray-400">total</div>
261+
</td>
262+
<td class="px-2 py-3 hidden xl:table-cell">
263+
<div class="font-medium text-white text-xs sm:text-sm">${run.stats.input_tokens ? (run.stats.input_tokens / 1000).toFixed(0) + 'k' : 'N/A'}</div>
264+
<div class="text-xs text-gray-400">input</div>
265+
</td>
266+
<td class="px-2 py-3 hidden xl:table-cell">
267+
<div class="font-medium text-white text-xs sm:text-sm">${run.stats.output_tokens ? (run.stats.output_tokens / 1000).toFixed(0) + 'k' : 'N/A'}</div>
268+
<div class="text-xs text-gray-400">output</div>
269+
</td>
270+
<td class="px-2 py-3 hidden lg:table-cell">
271+
<div class="font-medium text-white text-xs sm:text-sm">${run.stats.avg_response_time ? run.stats.avg_response_time.toFixed(1) + 's' : 'N/A'}</div>
272+
<div class="text-xs text-gray-400">avg time</div>
273+
</td>
274+
<td class="px-2 py-3">
275+
<div class="text-white text-xs sm:text-sm font-semibold">${run.stats.total_cost ? '$' + run.stats.total_cost.toFixed(3) : 'N/A'}</div>
276+
<div class="text-xs text-gray-400 lg:hidden">${(run.stats.run_duration_seconds / 60).toFixed(1)}m</div>
277+
</td>
278+
</tr>
279+
<tr id="community-stats-row-${index}" class="hidden">
280+
<td colspan="13" class="px-2 py-4 bg-gray-800">
281+
${renderCommunityDetailedStats(run, index)}
282+
</td>
283+
</tr>
284+
`;
285+
}).join('');
286+
}
287+
288+
// Render detailed stats for community run
289+
function renderCommunityDetailedStats(run, index) {
290+
return `
291+
<div class="px-2 sm:px-4 w-full overflow-hidden">
292+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
293+
<div class="bg-gray-700 rounded-lg p-3 sm:p-4 min-w-0">
294+
<h4 class="font-semibold text-white mb-1.5 sm:mb-2 text-sm sm:text-base lg:text-base">Configuration</h4>
295+
<div class="space-y-1.5 sm:space-y-2 text-gray-300 text-sm sm:text-sm lg:text-sm">
296+
${Object.entries(run.config).map(([key, value]) => `
297+
<div class="flex justify-between items-center min-w-0">
298+
<span class="truncate mr-2 capitalize">${key.replace(/_/g, ' ')}:</span>
299+
<span class="text-white font-medium flex-shrink-0">${typeof value === 'object' ? JSON.stringify(value) : value}</span>
173300
</div>
174-
</div>
301+
`).join('')}
175302
</div>
176-
<p class="text-gray-300 mb-4">${submission.description}</p>
177-
<div class="flex items-center gap-2">
178-
<button class="border border-gray-600 text-gray-300 hover:border-gray-500 hover:text-white px-3 py-1 rounded text-sm transition-colors">
179-
View Details
180-
</button>
181-
<button class="text-gray-400 hover:text-green-400 px-3 py-1 text-sm transition-colors">
182-
<i class="fas fa-arrow-up"></i> Upvote
183-
</button>
184-
<button class="text-gray-400 hover:text-blue-400 px-3 py-1 text-sm transition-colors">
185-
Try Strategy
186-
</button>
303+
</div>
304+
<div class="bg-gray-700 rounded-lg p-3 sm:p-4 min-w-0">
305+
<h4 class="font-semibold text-white mb-1.5 sm:mb-2 text-sm sm:text-base lg:text-base">Statistics</h4>
306+
<div class="space-y-1.5 sm:space-y-2 text-gray-300 text-sm sm:text-sm lg:text-sm">
307+
${Object.entries(run.stats).slice(0, 15).map(([key, value]) => `
308+
<div class="flex justify-between items-center min-w-0">
309+
<span class="truncate mr-2 capitalize">${key.replace(/_/g, ' ')}:</span>
310+
<span class="text-white font-medium flex-shrink-0">${
311+
typeof value === 'number' && key.includes('time') ? (value / 60).toFixed(1) + 'm' :
312+
typeof value === 'number' && key.includes('rate') ? (value * 100).toFixed(1) + '%' :
313+
typeof value === 'object' ? JSON.stringify(value) : value
314+
}</span>
315+
</div>
316+
`).join('')}
187317
</div>
188318
</div>
189319
</div>
190-
`).join('');
191-
} catch (error) {
192-
console.error('Error loading community submissions:', error);
193-
const container = document.getElementById('community-submissions');
194-
container.innerHTML = '<p class="text-red-500">Error loading submissions. Please check the data files.</p>';
320+
</div>
321+
`;
322+
}
323+
324+
// Calculate a simple performance score for community runs
325+
function calculatePerformanceScore(stats) {
326+
// Simple scoring: ante_reached * 10 + money efficiency
327+
return stats.ante_reached * 10 + (stats.final_money * 0.1);
328+
}
329+
330+
// Show error message
331+
function showError(message) {
332+
const tbody = document.getElementById('community-leaderboard-body');
333+
if (tbody) {
334+
tbody.innerHTML = `<tr><td colspan="13" class="px-2 py-4 text-center text-gray-400 text-sm">${message}</td></tr>`;
335+
}
336+
}
337+
338+
// Utility function for rank colors
339+
function getRankColor(rank) {
340+
switch(rank) {
341+
case 1: return 'text-yellow-400'; // Gold
342+
case 2: return 'text-gray-300'; // Silver
343+
case 3: return 'text-orange-400'; // Bronze
344+
default: return 'text-gray-400';
345+
}
346+
}
347+
348+
// Toggle statistics display for a community run
349+
function toggleCommunityStats(index) {
350+
const statsRow = document.getElementById(`community-stats-row-${index}`);
351+
352+
if (statsRow) {
353+
if (statsRow.classList.contains('hidden')) {
354+
statsRow.classList.remove('hidden');
355+
} else {
356+
statsRow.classList.add('hidden');
357+
}
195358
}
196359
}
197360

198361
// Load data when page loads
199-
document.addEventListener('DOMContentLoaded', loadCommunitySubmissions);
362+
document.addEventListener('DOMContentLoaded', loadCommunityLeaderboard);
200363
</script>
201364
</body>
202365
</html>

0 commit comments

Comments
 (0)