David Pomerenke
Rename
7fcc452
raw
history blame
10.6 kB
<!DOCTYPE html>
<html>
<head>
<title>AI 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>
<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 bg-white shadow-lg py-4 mx-4 rounded-lg border border-gray-200">
<div class="flex flex-col">
<h3 class="px-6 py-2 text-gray-400 text-sm font-medium">Navigation</h3>
<a href="#" onclick="showSection('coverage'); toggleMobileMenu()" class="nav-link px-6 py-3 text-gray-600 hover:bg-gray-50">
Language Coverage
</a>
<a href="#" onclick="showSection('comparison'); toggleMobileMenu()" class="nav-link px-6 py-3 text-gray-600 hover:bg-gray-50">
LLM Comparison
</a>
<a href="#" onclick="showSection('results'); toggleMobileMenu()" class="nav-link px-6 py-3 text-gray-600 hover:bg-gray-50">
Results by Language
</a>
</div>
</div>
<!-- Desktop menu -->
<div class="hidden sm:flex justify-center h-16 border-b border-gray-200">
<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>
<!--
- Leaderboard
- Filters
- commercial vs open source
- Eval results per task (across all languages)
- Timeline
-->
</section>
<section id="results" class="section hidden">
<div id="language-list"></div>
<!--
- Filters
- free-text search
- by continent, by language family
- sort by: population ><, performance ><, datasets ><
- Language list with details
- Eval results for each task and model
- Available datasets
- Form field to submit more datasets and custom models
-->
</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>