David Pomerenke
commited on
Commit
Β·
698d104
1
Parent(s):
790c5f2
Basic Observable Framework setup
Browse files- .gitignore +6 -0
- index.html +0 -254
- observablehq.config.js +38 -0
- package-lock.json +0 -0
- package.json +22 -0
- results.json +0 -0
- src/.gitignore +1 -0
- src/chart.md +85 -0
- src/components/timeline.js +16 -0
- src/data/languagebench.json +62 -0
- languagebench.py β src/data/languagebench.json.py +8 -8
- src/index.md +111 -0
- src/list.md +71 -0
- src/observable.png +0 -0
.gitignore
CHANGED
@@ -4,6 +4,12 @@ ScriptCodes.csv
|
|
4 |
.cache
|
5 |
.env
|
6 |
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
# Python-generated files
|
8 |
__pycache__/
|
9 |
*.py[oc]
|
|
|
4 |
.cache
|
5 |
.env
|
6 |
|
7 |
+
# Observable
|
8 |
+
.DS_Store
|
9 |
+
/dist/
|
10 |
+
node_modules/
|
11 |
+
yarn-error.log
|
12 |
+
|
13 |
# Python-generated files
|
14 |
__pycache__/
|
15 |
*.py[oc]
|
index.html
DELETED
@@ -1,254 +0,0 @@
|
|
1 |
-
<!DOCTYPE html>
|
2 |
-
<html>
|
3 |
-
|
4 |
-
<head>
|
5 |
-
<title>AI Language Monitor</title>
|
6 |
-
<script src="https://cdn.tailwindcss.com"></script>
|
7 |
-
<style>
|
8 |
-
body {
|
9 |
-
margin: 0 auto;
|
10 |
-
padding: 20px;
|
11 |
-
font-family: sans-serif;
|
12 |
-
}
|
13 |
-
|
14 |
-
.language-header {
|
15 |
-
margin-bottom: 10px;
|
16 |
-
}
|
17 |
-
|
18 |
-
.speaker-count {
|
19 |
-
font-size: 0.8em;
|
20 |
-
color: #666;
|
21 |
-
font-weight: normal;
|
22 |
-
margin: 0;
|
23 |
-
}
|
24 |
-
</style>
|
25 |
-
<link rel="icon"
|
26 |
-
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>">
|
27 |
-
</head>
|
28 |
-
|
29 |
-
<body>
|
30 |
-
<nav>
|
31 |
-
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
32 |
-
<!-- Mobile menu button -->
|
33 |
-
<div class="sm:hidden absolute left-4 top-4">
|
34 |
-
<button onclick="toggleMobileMenu()" class="text-gray-500 hover:text-gray-700 focus:outline-none">
|
35 |
-
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
36 |
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
37 |
-
</svg>
|
38 |
-
</button>
|
39 |
-
</div>
|
40 |
-
|
41 |
-
<!-- Mobile menu (hidden by default) -->
|
42 |
-
<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">
|
43 |
-
<div class="flex flex-col">
|
44 |
-
<h3 class="px-6 py-2 text-gray-400 text-sm font-medium">Navigation</h3>
|
45 |
-
<a href="#" onclick="showSection('coverage'); toggleMobileMenu()" class="nav-link px-6 py-3 text-gray-600 hover:bg-gray-50">
|
46 |
-
Language Coverage
|
47 |
-
</a>
|
48 |
-
<a href="#" onclick="showSection('comparison'); toggleMobileMenu()" class="nav-link px-6 py-3 text-gray-600 hover:bg-gray-50">
|
49 |
-
LLM Comparison
|
50 |
-
</a>
|
51 |
-
<a href="#" onclick="showSection('results'); toggleMobileMenu()" class="nav-link px-6 py-3 text-gray-600 hover:bg-gray-50">
|
52 |
-
Results by Language
|
53 |
-
</a>
|
54 |
-
</div>
|
55 |
-
</div>
|
56 |
-
|
57 |
-
<!-- Desktop menu -->
|
58 |
-
<div class="hidden sm:flex justify-center h-16 border-b border-gray-200">
|
59 |
-
<div class="flex">
|
60 |
-
<div class="flex space-x-8">
|
61 |
-
<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">
|
62 |
-
Language Coverage
|
63 |
-
</a>
|
64 |
-
<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">
|
65 |
-
LLM Comparison
|
66 |
-
</a>
|
67 |
-
<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">
|
68 |
-
Results by Language
|
69 |
-
</a>
|
70 |
-
</div>
|
71 |
-
</div>
|
72 |
-
</div>
|
73 |
-
</div>
|
74 |
-
</nav>
|
75 |
-
|
76 |
-
<div class="p-6">
|
77 |
-
<section id="coverage" class="section">
|
78 |
-
<div id="summary-chart"></div>
|
79 |
-
</section>
|
80 |
-
|
81 |
-
<section id="comparison" class="section hidden">
|
82 |
-
<p class="text-gray-600">Coming soon...</p>
|
83 |
-
<!--
|
84 |
-
- Leaderboard
|
85 |
-
- Filters
|
86 |
-
- commercial vs open source
|
87 |
-
- Eval results per task (across all languages)
|
88 |
-
- Timeline
|
89 |
-
-->
|
90 |
-
</section>
|
91 |
-
|
92 |
-
<section id="results" class="section hidden">
|
93 |
-
<div id="language-list"></div>
|
94 |
-
<!--
|
95 |
-
- Filters
|
96 |
-
- free-text search
|
97 |
-
- by continent, by language family
|
98 |
-
- sort by: population ><, performance ><, datasets ><
|
99 |
-
- Language list with details
|
100 |
-
- Eval results for each task and model
|
101 |
-
- Available datasets
|
102 |
-
- Form field to submit more datasets and custom models
|
103 |
-
-->
|
104 |
-
</section>
|
105 |
-
</div>
|
106 |
-
|
107 |
-
<script type="module">
|
108 |
-
// Import Plot using ESM
|
109 |
-
import * as Plot from "https://cdn.jsdelivr.net/npm/@observablehq/plot@0.6/+esm";
|
110 |
-
|
111 |
-
function showSection(sectionId) {
|
112 |
-
// Update nav links
|
113 |
-
document.querySelectorAll('.nav-link').forEach(link => {
|
114 |
-
link.classList.remove('border-indigo-500', 'text-gray-900');
|
115 |
-
link.classList.add('border-transparent', 'text-gray-500');
|
116 |
-
});
|
117 |
-
const activeLink = document.querySelector(`[onclick="showSection('${sectionId}')"]`);
|
118 |
-
activeLink.classList.remove('border-transparent', 'text-gray-500');
|
119 |
-
activeLink.classList.add('border-indigo-500', 'text-gray-900');
|
120 |
-
|
121 |
-
// Show/hide sections
|
122 |
-
document.querySelectorAll('.section').forEach(section => {
|
123 |
-
section.classList.add('hidden');
|
124 |
-
});
|
125 |
-
document.getElementById(sectionId).classList.remove('hidden');
|
126 |
-
}
|
127 |
-
window.showSection = showSection;
|
128 |
-
|
129 |
-
function toggleMobileMenu() {
|
130 |
-
const mobileMenu = document.getElementById('mobileMenu');
|
131 |
-
mobileMenu.classList.toggle('hidden');
|
132 |
-
}
|
133 |
-
window.toggleMobileMenu = toggleMobileMenu;
|
134 |
-
|
135 |
-
async function init() {
|
136 |
-
const scoreKey = "bleu"
|
137 |
-
const scoreName = "BLEU Score"
|
138 |
-
const summaryChartDiv = document.getElementById('summary-chart');
|
139 |
-
const languageListDiv = document.getElementById('language-list');
|
140 |
-
|
141 |
-
const response = await fetch('results.json');
|
142 |
-
const data = await response.json();
|
143 |
-
// Format captions
|
144 |
-
const formatScore = (score) => score > 0 ? score.toFixed(2) : "No benchmark available!"
|
145 |
-
const formatTitle = d => (d.language_name + "\n" + parseInt(d.speakers / 1_000_00) / 10 + "M speakers\n" + scoreName + ": " + formatScore(d[scoreKey]))
|
146 |
-
|
147 |
-
// Create summary plot
|
148 |
-
const summaryPlot = Plot.plot({
|
149 |
-
width: summaryChartDiv.clientWidth,
|
150 |
-
height: 400,
|
151 |
-
marginBottom: 100,
|
152 |
-
x: { label: "Number of speakers", axis: null },
|
153 |
-
y: { label: `${scoreName} (average across models)` },
|
154 |
-
// color: { scheme: "BrBG" },
|
155 |
-
marks: [
|
156 |
-
Plot.rectY(data, Plot.stackX({
|
157 |
-
x: "speakers",
|
158 |
-
order: scoreKey,
|
159 |
-
reverse: true,
|
160 |
-
y2: scoreKey, // y2 to avoid stacking by y
|
161 |
-
title: formatTitle,
|
162 |
-
tip: true,
|
163 |
-
fill: d => d[scoreKey] > 0 ? "black" : "pink"
|
164 |
-
})),
|
165 |
-
Plot.rectY(data, Plot.pointerX(Plot.stackX({
|
166 |
-
x: "speakers",
|
167 |
-
order: scoreKey,
|
168 |
-
reverse: true,
|
169 |
-
y2: scoreKey, // y2 to avoid stacking by y
|
170 |
-
fill: "grey",
|
171 |
-
}))),
|
172 |
-
Plot.text(data, Plot.stackX({
|
173 |
-
x: "speakers",
|
174 |
-
y2: scoreKey,
|
175 |
-
order: scoreKey,
|
176 |
-
reverse: true,
|
177 |
-
text: "language_name",
|
178 |
-
frameAnchor: "bottom",
|
179 |
-
textAnchor: "end",
|
180 |
-
dy: 10,
|
181 |
-
rotate: 270,
|
182 |
-
opacity: (d) => d.speakers > 50_000_000 ? 1 : 0,
|
183 |
-
}))
|
184 |
-
]
|
185 |
-
});
|
186 |
-
|
187 |
-
// Add summary plot to the coverage section
|
188 |
-
summaryChartDiv.appendChild(summaryPlot);
|
189 |
-
|
190 |
-
// Get unique languages with their speaker counts
|
191 |
-
const languageMap = new Map();
|
192 |
-
data.forEach(r => {
|
193 |
-
if (!languageMap.has(r.language_name)) {
|
194 |
-
languageMap.set(r.language_name, r.speakers);
|
195 |
-
}
|
196 |
-
});
|
197 |
-
|
198 |
-
// Sort languages by speaker count (descending)
|
199 |
-
const languages = [...languageMap.entries()]
|
200 |
-
.sort((a, b) => b[1] - a[1])
|
201 |
-
.map(([lang]) => lang);
|
202 |
-
|
203 |
-
// Section for each language
|
204 |
-
languages.forEach(language => {
|
205 |
-
const headerDiv = document.createElement('div');
|
206 |
-
headerDiv.className = 'language-header';
|
207 |
-
|
208 |
-
const h2 = document.createElement('h2');
|
209 |
-
h2.textContent = language;
|
210 |
-
h2.style.marginBottom = '5px';
|
211 |
-
|
212 |
-
const speakerP = document.createElement('p');
|
213 |
-
speakerP.className = 'speaker-count';
|
214 |
-
const speakerCount = (languageMap.get(language) / 1_000_000).toFixed(1);
|
215 |
-
speakerP.textContent = `${speakerCount}M speakers`;
|
216 |
-
|
217 |
-
headerDiv.appendChild(h2);
|
218 |
-
headerDiv.appendChild(speakerP);
|
219 |
-
languageListDiv.appendChild(headerDiv);
|
220 |
-
|
221 |
-
const languageData = data.filter(r => r.language_name === language)[0]["scores"];
|
222 |
-
|
223 |
-
const descriptor = code => {
|
224 |
-
let [org, model] = code.split("/")
|
225 |
-
return model.split("-")[0]
|
226 |
-
}
|
227 |
-
|
228 |
-
// Plot for how well the models perform on this language
|
229 |
-
if (languageData && languageData.length > 1) {
|
230 |
-
const plot = Plot.plot({
|
231 |
-
width: 400,
|
232 |
-
height: 200,
|
233 |
-
margin: 30,
|
234 |
-
y: {
|
235 |
-
domain: [0, 1],
|
236 |
-
label: scoreName
|
237 |
-
},
|
238 |
-
marks: [
|
239 |
-
Plot.barY(languageData, {
|
240 |
-
x: d => descriptor(d.model),
|
241 |
-
y: scoreKey
|
242 |
-
})
|
243 |
-
]
|
244 |
-
});
|
245 |
-
languageListDiv.appendChild(plot);
|
246 |
-
}
|
247 |
-
});
|
248 |
-
}
|
249 |
-
|
250 |
-
init();
|
251 |
-
</script>
|
252 |
-
</body>
|
253 |
-
|
254 |
-
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
observablehq.config.js
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// See https://observablehq.com/framework/config for documentation.
|
2 |
+
export default {
|
3 |
+
// The appβs title; used in the sidebar and webpage titles.
|
4 |
+
title: "AI Language Monitor",
|
5 |
+
|
6 |
+
// The pages and sections in the sidebar. If you donβt specify this option,
|
7 |
+
// all pages will be listed in alphabetical order. Listing pages explicitly
|
8 |
+
// lets you organize them into sections and have unlisted pages.
|
9 |
+
// pages: [
|
10 |
+
// {
|
11 |
+
// name: "Examples",
|
12 |
+
// pages: [
|
13 |
+
// {name: "Dashboard", path: "/example-dashboard"},
|
14 |
+
// {name: "Report", path: "/example-report"}
|
15 |
+
// ]
|
16 |
+
// }
|
17 |
+
// ],
|
18 |
+
|
19 |
+
// Content to add to the head of the page, e.g. for a favicon:
|
20 |
+
head: '<link rel="icon" href="observable.png" type="image/png" sizes="32x32">',
|
21 |
+
|
22 |
+
// The path to the source root.
|
23 |
+
root: "src",
|
24 |
+
|
25 |
+
// Some additional configuration options and their defaults:
|
26 |
+
// theme: "default", // try "light", "dark", "slate", etc.
|
27 |
+
// header: "", // what to show in the header (HTML)
|
28 |
+
// footer: "Built with Observable.", // what to show in the footer (HTML)
|
29 |
+
// sidebar: true, // whether to show the sidebar
|
30 |
+
// toc: true, // whether to show the table of contents
|
31 |
+
// pager: true, // whether to show previous & next links in the footer
|
32 |
+
// output: "dist", // path to the output root for build
|
33 |
+
// search: true, // activate search
|
34 |
+
// linkify: true, // convert URLs in Markdown to links
|
35 |
+
// typographer: false, // smart quotes and other typographic improvements
|
36 |
+
// preserveExtension: false, // drop .html from URLs
|
37 |
+
// preserveIndex: false, // drop /index from URLs
|
38 |
+
};
|
package-lock.json
ADDED
The diff for this file is too large to render.
See raw diff
|
|
package.json
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"type": "module",
|
3 |
+
"private": true,
|
4 |
+
"scripts": {
|
5 |
+
"clean": "rimraf src/.observablehq/cache",
|
6 |
+
"build": "observable build",
|
7 |
+
"dev": "observable preview",
|
8 |
+
"deploy": "observable deploy",
|
9 |
+
"observable": "observable"
|
10 |
+
},
|
11 |
+
"dependencies": {
|
12 |
+
"@observablehq/framework": "^1.13.2",
|
13 |
+
"d3-dsv": "^3.0.1",
|
14 |
+
"d3-time-format": "^4.1.0"
|
15 |
+
},
|
16 |
+
"devDependencies": {
|
17 |
+
"rimraf": "^5.0.5"
|
18 |
+
},
|
19 |
+
"engines": {
|
20 |
+
"node": ">=18"
|
21 |
+
}
|
22 |
+
}
|
results.json
DELETED
The diff for this file is too large to render.
See raw diff
|
|
src/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
/.observablehq/cache/
|
src/chart.md
ADDED
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
theme: dashboard
|
3 |
+
title: AI Language Monitor
|
4 |
+
toc: false
|
5 |
+
head: <link rel="icon"
|
6 |
+
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>">
|
7 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet">
|
8 |
+
<!--<link rel="stylesheet" href="styles.css">-->
|
9 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
10 |
+
---
|
11 |
+
|
12 |
+
# AI Language Monitor
|
13 |
+
|
14 |
+
```js
|
15 |
+
const data = FileAttachment("data/languagebench.json").json();
|
16 |
+
```
|
17 |
+
|
18 |
+
```js
|
19 |
+
const scoreKey = "bleu"
|
20 |
+
const scoreName = "BLEU Score"
|
21 |
+
|
22 |
+
// Format captions
|
23 |
+
const formatScore = (score) => score > 0 ? score.toFixed(2) : "No benchmark available!"
|
24 |
+
const formatTitle = d => (d.language_name + "\n" + parseInt(d.speakers / 1_000_00) / 10 + "M speakers\n" + scoreName + ": " + formatScore(d[scoreKey]))
|
25 |
+
|
26 |
+
// Create summary plot
|
27 |
+
const chart = Plot.plot({
|
28 |
+
width: 600,
|
29 |
+
height: 400,
|
30 |
+
marginBottom: 100,
|
31 |
+
x: { label: "Number of speakers", axis: null },
|
32 |
+
y: { label: `${scoreName} (average across models)` },
|
33 |
+
// color: { scheme: "BrBG" },
|
34 |
+
marks: [
|
35 |
+
Plot.rectY(data, Plot.stackX({
|
36 |
+
x: "speakers",
|
37 |
+
order: scoreKey,
|
38 |
+
reverse: true,
|
39 |
+
y2: scoreKey, // y2 to avoid stacking by y
|
40 |
+
title: formatTitle,
|
41 |
+
tip: true,
|
42 |
+
fill: d => d[scoreKey] > 0 ? "black" : "pink"
|
43 |
+
})),
|
44 |
+
Plot.rectY(data, Plot.pointerX(Plot.stackX({
|
45 |
+
x: "speakers",
|
46 |
+
order: scoreKey,
|
47 |
+
reverse: true,
|
48 |
+
y2: scoreKey, // y2 to avoid stacking by y
|
49 |
+
fill: "grey",
|
50 |
+
}))),
|
51 |
+
Plot.text(data, Plot.stackX({
|
52 |
+
x: "speakers",
|
53 |
+
y2: scoreKey,
|
54 |
+
order: scoreKey,
|
55 |
+
reverse: true,
|
56 |
+
text: "language_name",
|
57 |
+
frameAnchor: "bottom",
|
58 |
+
textAnchor: "end",
|
59 |
+
dy: 10,
|
60 |
+
rotate: 270,
|
61 |
+
opacity: (d) => d.speakers > 50_000_000 ? 1 : 0,
|
62 |
+
}))
|
63 |
+
]
|
64 |
+
});
|
65 |
+
display(chart);
|
66 |
+
```
|
67 |
+
|
68 |
+
<style>
|
69 |
+
body {
|
70 |
+
margin: 0 auto;
|
71 |
+
padding: 20px;
|
72 |
+
font-family: sans-serif;
|
73 |
+
}
|
74 |
+
|
75 |
+
.language-header {
|
76 |
+
margin-bottom: 10px;
|
77 |
+
}
|
78 |
+
|
79 |
+
.speaker-count {
|
80 |
+
font-size: 0.8em;
|
81 |
+
color: #666;
|
82 |
+
font-weight: normal;
|
83 |
+
margin: 0;
|
84 |
+
}
|
85 |
+
</style>
|
src/components/timeline.js
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import * as Plot from "npm:@observablehq/plot";
|
2 |
+
|
3 |
+
export function timeline(events, {width, height} = {}) {
|
4 |
+
return Plot.plot({
|
5 |
+
width,
|
6 |
+
height,
|
7 |
+
marginTop: 30,
|
8 |
+
x: {nice: true, label: null, tickFormat: ""},
|
9 |
+
y: {axis: null},
|
10 |
+
marks: [
|
11 |
+
Plot.ruleX(events, {x: "year", y: "y", markerEnd: "dot", strokeWidth: 2.5}),
|
12 |
+
Plot.ruleY([0]),
|
13 |
+
Plot.text(events, {x: "year", y: "y", text: "name", lineAnchor: "bottom", dy: -10, lineWidth: 10, fontSize: 12})
|
14 |
+
]
|
15 |
+
});
|
16 |
+
}
|
src/data/languagebench.json
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[
|
2 |
+
{
|
3 |
+
"language_name": "English",
|
4 |
+
"language_code": "eng",
|
5 |
+
"speakers": 1132366680.0,
|
6 |
+
"scores": [
|
7 |
+
{
|
8 |
+
"model": "anthropic/claude-3.5-haiku",
|
9 |
+
"bleu": 0.4114123099745433
|
10 |
+
}
|
11 |
+
],
|
12 |
+
"bleu": 0.4114123099745433
|
13 |
+
},
|
14 |
+
{
|
15 |
+
"language_name": "Mandarin Chinese",
|
16 |
+
"language_code": "cmn",
|
17 |
+
"speakers": 1074000000.0,
|
18 |
+
"scores": [
|
19 |
+
{
|
20 |
+
"model": "anthropic/claude-3.5-haiku",
|
21 |
+
"bleu": 0.22799274850984375
|
22 |
+
}
|
23 |
+
],
|
24 |
+
"bleu": 0.22799274850984375
|
25 |
+
},
|
26 |
+
{
|
27 |
+
"language_name": "Spanish",
|
28 |
+
"language_code": "spa",
|
29 |
+
"speakers": 485000000.0,
|
30 |
+
"scores": [
|
31 |
+
{
|
32 |
+
"model": "anthropic/claude-3.5-haiku",
|
33 |
+
"bleu": 0.27814703404841756
|
34 |
+
}
|
35 |
+
],
|
36 |
+
"bleu": 0.27814703404841756
|
37 |
+
},
|
38 |
+
{
|
39 |
+
"language_name": "Hindi",
|
40 |
+
"language_code": "hin",
|
41 |
+
"speakers": 341000000.0,
|
42 |
+
"scores": [
|
43 |
+
{
|
44 |
+
"model": "anthropic/claude-3.5-haiku",
|
45 |
+
"bleu": 0.2607691459848629
|
46 |
+
}
|
47 |
+
],
|
48 |
+
"bleu": 0.2607691459848629
|
49 |
+
},
|
50 |
+
{
|
51 |
+
"language_name": "Bengali",
|
52 |
+
"language_code": "ben",
|
53 |
+
"speakers": 300000000.0,
|
54 |
+
"scores": [
|
55 |
+
{
|
56 |
+
"model": "anthropic/claude-3.5-haiku",
|
57 |
+
"bleu": 0.2504671437388243
|
58 |
+
}
|
59 |
+
],
|
60 |
+
"bleu": 0.2504671437388243
|
61 |
+
}
|
62 |
+
]
|
languagebench.py β src/data/languagebench.json.py
RENAMED
@@ -15,14 +15,14 @@ from transformers import NllbTokenizer
|
|
15 |
|
16 |
# config
|
17 |
models = [
|
18 |
-
"openai/gpt-4o",
|
19 |
-
"anthropic/claude-3.5-
|
20 |
-
"meta-llama/llama-3.1-405b-instruct", # lots of slow repetitions for LRLs
|
21 |
-
"mistralai/mistral-large",
|
22 |
# "google/gemini-flash-1.5", # very fast
|
23 |
-
"qwen/qwen-2.5-72b-instruct", # somewhat slow
|
24 |
]
|
25 |
-
fast_model = "anthropic/claude-3.5-
|
26 |
n_sentences = 30
|
27 |
|
28 |
# setup
|
@@ -160,7 +160,7 @@ def load_sentences(language):
|
|
160 |
# evaluation!
|
161 |
async def main():
|
162 |
results = []
|
163 |
-
for language in languages.itertuples():
|
164 |
name = (
|
165 |
language.language_name
|
166 |
if not pd.isna(language.language_name)
|
@@ -220,7 +220,7 @@ async def main():
|
|
220 |
# "bert_score": mean([s["bert_score"] for s in scores]),
|
221 |
}
|
222 |
)
|
223 |
-
with open("
|
224 |
json.dump(results, f, indent=2, ensure_ascii=False)
|
225 |
|
226 |
|
|
|
15 |
|
16 |
# config
|
17 |
models = [
|
18 |
+
"openai/gpt-4o-mini",
|
19 |
+
"anthropic/claude-3.5-haiku",
|
20 |
+
# "meta-llama/llama-3.1-405b-instruct", # lots of slow repetitions for LRLs
|
21 |
+
# "mistralai/mistral-large",
|
22 |
# "google/gemini-flash-1.5", # very fast
|
23 |
+
# "qwen/qwen-2.5-72b-instruct", # somewhat slow
|
24 |
]
|
25 |
+
fast_model = "anthropic/claude-3.5-haiku"
|
26 |
n_sentences = 30
|
27 |
|
28 |
# setup
|
|
|
160 |
# evaluation!
|
161 |
async def main():
|
162 |
results = []
|
163 |
+
for language in list(languages.itertuples())[:5]:
|
164 |
name = (
|
165 |
language.language_name
|
166 |
if not pd.isna(language.language_name)
|
|
|
220 |
# "bert_score": mean([s["bert_score"] for s in scores]),
|
221 |
}
|
222 |
)
|
223 |
+
with open("src/data/languagebench.json", "w") as f:
|
224 |
json.dump(results, f, indent=2, ensure_ascii=False)
|
225 |
|
226 |
|
src/index.md
ADDED
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
toc: false
|
3 |
+
---
|
4 |
+
|
5 |
+
<div class="hero">
|
6 |
+
<h1>AI Language Monitor</h1>
|
7 |
+
<h2>Welcome to your new app! Edit <code style="font-size: 90%;">src/index.md</code> to change this page.</h2>
|
8 |
+
<a href="https://observablehq.com/framework/getting-started">Get started<span style="display: inline-block; margin-left: 0.25rem;">βοΈ</span></a>
|
9 |
+
</div>
|
10 |
+
|
11 |
+
<div class="grid grid-cols-2" style="grid-auto-rows: 504px;">
|
12 |
+
<div class="card">${
|
13 |
+
resize((width) => Plot.plot({
|
14 |
+
title: "Your awesomeness over time π",
|
15 |
+
subtitle: "Up and to the right!",
|
16 |
+
width,
|
17 |
+
y: {grid: true, label: "Awesomeness"},
|
18 |
+
marks: [
|
19 |
+
Plot.ruleY([0]),
|
20 |
+
Plot.lineY(aapl, {x: "Date", y: "Close", tip: true})
|
21 |
+
]
|
22 |
+
}))
|
23 |
+
}</div>
|
24 |
+
<div class="card">${
|
25 |
+
resize((width) => Plot.plot({
|
26 |
+
title: "How big are penguins, anyway? π§",
|
27 |
+
width,
|
28 |
+
grid: true,
|
29 |
+
x: {label: "Body mass (g)"},
|
30 |
+
y: {label: "Flipper length (mm)"},
|
31 |
+
color: {legend: true},
|
32 |
+
marks: [
|
33 |
+
Plot.linearRegressionY(penguins, {x: "body_mass_g", y: "flipper_length_mm", stroke: "species"}),
|
34 |
+
Plot.dot(penguins, {x: "body_mass_g", y: "flipper_length_mm", stroke: "species", tip: true})
|
35 |
+
]
|
36 |
+
}))
|
37 |
+
}</div>
|
38 |
+
</div>
|
39 |
+
|
40 |
+
---
|
41 |
+
|
42 |
+
## Next steps
|
43 |
+
|
44 |
+
Here are some ideas of things you could tryβ¦
|
45 |
+
|
46 |
+
<div class="grid grid-cols-4">
|
47 |
+
<div class="card">
|
48 |
+
Chart your own data using <a href="https://observablehq.com/framework/lib/plot"><code>Plot</code></a> and <a href="https://observablehq.com/framework/files"><code>FileAttachment</code></a>. Make it responsive using <a href="https://observablehq.com/framework/javascript#resize(render)"><code>resize</code></a>.
|
49 |
+
</div>
|
50 |
+
<div class="card">
|
51 |
+
Create a <a href="https://observablehq.com/framework/project-structure">new page</a> by adding a Markdown file (<code>whatever.md</code>) to the <code>src</code> folder.
|
52 |
+
</div>
|
53 |
+
<div class="card">
|
54 |
+
Add a drop-down menu using <a href="https://observablehq.com/framework/inputs/select"><code>Inputs.select</code></a> and use it to filter the data shown in a chart.
|
55 |
+
</div>
|
56 |
+
<div class="card">
|
57 |
+
Write a <a href="https://observablehq.com/framework/loaders">data loader</a> that queries a local database or API, generating a data snapshot on build.
|
58 |
+
</div>
|
59 |
+
<div class="card">
|
60 |
+
Import a <a href="https://observablehq.com/framework/imports">recommended library</a> from npm, such as <a href="https://observablehq.com/framework/lib/leaflet">Leaflet</a>, <a href="https://observablehq.com/framework/lib/dot">GraphViz</a>, <a href="https://observablehq.com/framework/lib/tex">TeX</a>, or <a href="https://observablehq.com/framework/lib/duckdb">DuckDB</a>.
|
61 |
+
</div>
|
62 |
+
<div class="card">
|
63 |
+
Ask for help, or share your work or ideas, on our <a href="https://github.com/observablehq/framework/discussions">GitHub discussions</a>.
|
64 |
+
</div>
|
65 |
+
<div class="card">
|
66 |
+
Visit <a href="https://github.com/observablehq/framework">Framework on GitHub</a> and give us a star. Or file an issue if youβve found a bug!
|
67 |
+
</div>
|
68 |
+
</div>
|
69 |
+
|
70 |
+
<style>
|
71 |
+
|
72 |
+
.hero {
|
73 |
+
display: flex;
|
74 |
+
flex-direction: column;
|
75 |
+
align-items: center;
|
76 |
+
font-family: var(--sans-serif);
|
77 |
+
margin: 4rem 0 8rem;
|
78 |
+
text-wrap: balance;
|
79 |
+
text-align: center;
|
80 |
+
}
|
81 |
+
|
82 |
+
.hero h1 {
|
83 |
+
margin: 1rem 0;
|
84 |
+
padding: 1rem 0;
|
85 |
+
max-width: none;
|
86 |
+
font-size: 14vw;
|
87 |
+
font-weight: 900;
|
88 |
+
line-height: 1;
|
89 |
+
background: linear-gradient(30deg, var(--theme-foreground-focus), currentColor);
|
90 |
+
-webkit-background-clip: text;
|
91 |
+
-webkit-text-fill-color: transparent;
|
92 |
+
background-clip: text;
|
93 |
+
}
|
94 |
+
|
95 |
+
.hero h2 {
|
96 |
+
margin: 0;
|
97 |
+
max-width: 34em;
|
98 |
+
font-size: 20px;
|
99 |
+
font-style: initial;
|
100 |
+
font-weight: 500;
|
101 |
+
line-height: 1.5;
|
102 |
+
color: var(--theme-foreground-muted);
|
103 |
+
}
|
104 |
+
|
105 |
+
@media (min-width: 640px) {
|
106 |
+
.hero h1 {
|
107 |
+
font-size: 90px;
|
108 |
+
}
|
109 |
+
}
|
110 |
+
|
111 |
+
</style>
|
src/list.md
ADDED
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
theme: dashboard
|
3 |
+
title: List
|
4 |
+
toc: false
|
5 |
+
head: <link rel="icon"
|
6 |
+
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>">
|
7 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet">
|
8 |
+
<!--<link rel="stylesheet" href="styles.css">-->
|
9 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
10 |
+
---
|
11 |
+
|
12 |
+
# AI Language Monitor
|
13 |
+
|
14 |
+
```js
|
15 |
+
const data = FileAttachment("data/languagebench.json").json();
|
16 |
+
```
|
17 |
+
|
18 |
+
```js
|
19 |
+
const scoreKey = "bleu"
|
20 |
+
const scoreName = "BLEU Score"
|
21 |
+
|
22 |
+
// Get unique languages with their speaker counts
|
23 |
+
const languageMap = new Map();
|
24 |
+
data.forEach(r => {
|
25 |
+
if (!languageMap.has(r.language_name)) {
|
26 |
+
languageMap.set(r.language_name, r.speakers);
|
27 |
+
}
|
28 |
+
});
|
29 |
+
|
30 |
+
// Sort languages by speaker count (descending)
|
31 |
+
const languages = [...languageMap.entries()]
|
32 |
+
.sort((a, b) => b[1] - a[1])
|
33 |
+
.map(([lang]) => lang);
|
34 |
+
|
35 |
+
// Section for each language
|
36 |
+
languages.forEach(language => {
|
37 |
+
display(html`<h2 class="language-header">${language}</h2>`)
|
38 |
+
|
39 |
+
const speakerCount = (languageMap.get(language) / 1_000_000).toFixed(1);
|
40 |
+
display(html`${speakerCount}M speakers`);
|
41 |
+
|
42 |
+
const languageData = data.filter(r => r.language_name === language)[0]["scores"];
|
43 |
+
console.log(languageData)
|
44 |
+
|
45 |
+
const descriptor = code => {
|
46 |
+
let [org, model] = code.split("/")
|
47 |
+
return model.split("-")[0]
|
48 |
+
}
|
49 |
+
|
50 |
+
// Plot for how well the models perform on this language
|
51 |
+
if (languageData && languageData.length >= 1) {
|
52 |
+
console.log("yes")
|
53 |
+
const chart = Plot.plot({
|
54 |
+
width: 400,
|
55 |
+
height: 200,
|
56 |
+
margin: 30,
|
57 |
+
y: {
|
58 |
+
domain: [0, 1],
|
59 |
+
label: scoreName
|
60 |
+
},
|
61 |
+
marks: [
|
62 |
+
Plot.barY(languageData, {
|
63 |
+
x: d => descriptor(d.model),
|
64 |
+
y: scoreKey
|
65 |
+
})
|
66 |
+
]
|
67 |
+
});
|
68 |
+
display(chart)
|
69 |
+
}
|
70 |
+
});
|
71 |
+
```
|
src/observable.png
ADDED
![]() |