David Pomerenke commited on
Commit
698d104
Β·
1 Parent(s): 790c5f2

Basic Observable Framework setup

Browse files
.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-sonnet",
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-sonnet"
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("results.json", "w") as f:
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&nbsp;<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