Spaces:
Running
Running
| <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/[email protected]/+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> |