Spaces:
Runtime error
Runtime error
<html> | |
<head> | |
<title>Local Language Monitor</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<style> | |
body { | |
margin: 0 auto; | |
padding: 20px; | |
font-family: sans-serif; | |
} | |
.language-header { | |
margin-bottom: 10px; | |
} | |
.speaker-count { | |
font-size: 0.8em; | |
color: #666; | |
font-weight: normal; | |
margin: 0; | |
} | |
</style> | |
<link rel="icon" | |
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22 fill=%22black%22>🌍</text></svg>"> | |
</head> | |
<body> | |
<nav class="border-b border-gray-200 bg-white"> | |
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
<!-- Mobile menu button --> | |
<div class="sm:hidden absolute left-4 top-4"> | |
<button onclick="toggleMobileMenu()" class="text-gray-500 hover:text-gray-700 focus:outline-none"> | |
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" /> | |
</svg> | |
</button> | |
</div> | |
<!-- Mobile menu (hidden by default) --> | |
<div id="mobileMenu" class="hidden sm:hidden absolute left-0 top-16 w-full bg-white border-b border-gray-200 py-2"> | |
<div class="flex flex-col space-y-2 px-4"> | |
<a href="#" onclick="showSection('coverage'); toggleMobileMenu()" class="nav-link block px-3 py-2 text-base font-medium text-gray-700"> | |
Language Coverage | |
</a> | |
<a href="#" onclick="showSection('comparison'); toggleMobileMenu()" class="nav-link block px-3 py-2 text-base font-medium text-gray-700"> | |
LLM Comparison | |
</a> | |
<a href="#" onclick="showSection('results'); toggleMobileMenu()" class="nav-link block px-3 py-2 text-base font-medium text-gray-700"> | |
Results by Language | |
</a> | |
</div> | |
</div> | |
<!-- Desktop menu --> | |
<div class="hidden sm:flex justify-center h-16"> | |
<div class="flex"> | |
<div class="flex space-x-8"> | |
<a href="#" onclick="showSection('coverage')" class="nav-link active inline-flex items-center px-1 pt-1 border-b-2 border-indigo-500 text-sm font-medium text-gray-900"> | |
Language Coverage | |
</a> | |
<a href="#" onclick="showSection('comparison')" class="nav-link inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700"> | |
LLM Comparison | |
</a> | |
<a href="#" onclick="showSection('results')" class="nav-link inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700"> | |
Results by Language | |
</a> | |
</div> | |
</div> | |
</div> | |
</div> | |
</nav> | |
<div class="p-6"> | |
<section id="coverage" class="section"> | |
<div id="summary-chart"></div> | |
</section> | |
<section id="comparison" class="section hidden"> | |
<p class="text-gray-600">Coming soon...</p> | |
</section> | |
<section id="results" class="section hidden"> | |
<div id="language-list"></div> | |
</section> | |
</div> | |
<script type="module"> | |
// Import Plot using ESM | |
import * as Plot from "https://cdn.jsdelivr.net/npm/@observablehq/plot@0.6/+esm"; | |
function showSection(sectionId) { | |
// Update nav links | |
document.querySelectorAll('.nav-link').forEach(link => { | |
link.classList.remove('border-indigo-500', 'text-gray-900'); | |
link.classList.add('border-transparent', 'text-gray-500'); | |
}); | |
const activeLink = document.querySelector(`[onclick="showSection('${sectionId}')"]`); | |
activeLink.classList.remove('border-transparent', 'text-gray-500'); | |
activeLink.classList.add('border-indigo-500', 'text-gray-900'); | |
// Show/hide sections | |
document.querySelectorAll('.section').forEach(section => { | |
section.classList.add('hidden'); | |
}); | |
document.getElementById(sectionId).classList.remove('hidden'); | |
} | |
window.showSection = showSection; | |
function toggleMobileMenu() { | |
const mobileMenu = document.getElementById('mobileMenu'); | |
mobileMenu.classList.toggle('hidden'); | |
} | |
window.toggleMobileMenu = toggleMobileMenu; | |
async function init() { | |
const scoreKey = "bleu" | |
const scoreName = "BLEU Score" | |
const summaryChartDiv = document.getElementById('summary-chart'); | |
const languageListDiv = document.getElementById('language-list'); | |
const response = await fetch('results.json'); | |
const data = await response.json(); | |
// Format captions | |
const formatScore = (score) => score > 0 ? score.toFixed(2) : "No benchmark available!" | |
const formatTitle = d => (d.language_name + "\n" + parseInt(d.speakers / 1_000_00) / 10 + "M speakers\n" + scoreName + ": " + formatScore(d[scoreKey])) | |
// Create summary plot | |
const summaryPlot = Plot.plot({ | |
width: summaryChartDiv.clientWidth, | |
height: 400, | |
marginBottom: 100, | |
x: { label: "Number of speakers", axis: null }, | |
y: { label: `${scoreName} (average across models)` }, | |
// color: { scheme: "BrBG" }, | |
marks: [ | |
Plot.rectY(data, Plot.stackX({ | |
x: "speakers", | |
order: scoreKey, | |
reverse: true, | |
y2: scoreKey, // y2 to avoid stacking by y | |
title: formatTitle, | |
tip: true, | |
fill: d => d[scoreKey] > 0 ? "black" : "pink" | |
})), | |
Plot.rectY(data, Plot.pointerX(Plot.stackX({ | |
x: "speakers", | |
order: scoreKey, | |
reverse: true, | |
y2: scoreKey, // y2 to avoid stacking by y | |
fill: "grey", | |
}))), | |
Plot.text(data, Plot.stackX({ | |
x: "speakers", | |
y2: scoreKey, | |
order: scoreKey, | |
reverse: true, | |
text: "language_name", | |
frameAnchor: "bottom", | |
textAnchor: "end", | |
dy: 10, | |
rotate: 270, | |
opacity: (d) => d.speakers > 50_000_000 ? 1 : 0, | |
})) | |
] | |
}); | |
// Add summary plot to the coverage section | |
summaryChartDiv.appendChild(summaryPlot); | |
// Get unique languages with their speaker counts | |
const languageMap = new Map(); | |
data.forEach(r => { | |
if (!languageMap.has(r.language_name)) { | |
languageMap.set(r.language_name, r.speakers); | |
} | |
}); | |
// Sort languages by speaker count (descending) | |
const languages = [...languageMap.entries()] | |
.sort((a, b) => b[1] - a[1]) | |
.map(([lang]) => lang); | |
// Section for each language | |
languages.forEach(language => { | |
const headerDiv = document.createElement('div'); | |
headerDiv.className = 'language-header'; | |
const h2 = document.createElement('h2'); | |
h2.textContent = language; | |
h2.style.marginBottom = '5px'; | |
const speakerP = document.createElement('p'); | |
speakerP.className = 'speaker-count'; | |
const speakerCount = (languageMap.get(language) / 1_000_000).toFixed(1); | |
speakerP.textContent = `${speakerCount}M speakers`; | |
headerDiv.appendChild(h2); | |
headerDiv.appendChild(speakerP); | |
languageListDiv.appendChild(headerDiv); | |
const languageData = data.filter(r => r.language_name === language)[0]["scores"]; | |
const descriptor = code => { | |
let [org, model] = code.split("/") | |
return model.split("-")[0] | |
} | |
// Plot for how well the models perform on this language | |
if (languageData && languageData.length > 1) { | |
const plot = Plot.plot({ | |
width: 400, | |
height: 200, | |
margin: 30, | |
y: { | |
domain: [0, 1], | |
label: scoreName | |
}, | |
marks: [ | |
Plot.barY(languageData, { | |
x: d => descriptor(d.model), | |
y: scoreKey | |
}) | |
] | |
}); | |
languageListDiv.appendChild(plot); | |
} | |
}); | |
} | |
init(); | |
</script> | |
</body> | |
</html> |