rtr-bench 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,14 +1,14 @@
1
1
  # RTR-bench
2
2
 
3
- [Mixed Messages](https://mixedmessages.fyi) is a generative art project that asks participants to interpret a daily prompt by placing a coordinate on a 2x2 grid, where the x-axis is Honest vs Performed, and the y-axis is Uniting vs Dividing. Across 175 windows, there have been almost 40k individual submissions. Each is a unique human data point.
3
+ **RTR-bench** is a benchmark that scores language models on cultural fluency, built from real human data. Its source is [Mixed Messages](https://mixedmessages.fyi), a generative art project that asks participants to interpret a daily prompt by placing a coordinate on a 2x2 grid, where the x-axis is Honest vs Performed and the y-axis is Uniting vs Dividing. Across 174 closed windows there have been almost 40k individual submissions. Each one is a unique, real human data point.
4
4
 
5
5
  Most labeled datasets are made by asking annotators to apply a rubric. This one was not. Nobody was given a definition of honest or performed, nobody was trained, and there was no correct answer to converge on. People arrived, interpreted a prompt, placed a submission, and left. What makes the data unusual is that the crowd's answer is a distribution, rather than a label. Not "people say X", but "31% of people said this, 44% said that, and the rest said something else."
6
6
 
7
- That distribution is what makes it useful for evaluating a model. Every window has a majority reading and a real minority, so a model can be scored on whether it lands with the crowd or against it, and on which axis it misreads when it does. There is also nothing here to memorize. The axes are cultural rather than factual, so no amount of training data tells a model how a crowd interpreted a prompt. The only way to get it right is for the model to understand how people would think and interpret.
7
+ That distribution is what makes it useful for evaluating a model. Every window has a dominant quadrant, so a model can be scored on whether it lands with the crowd or against it, and on which axis it misreads when it does. There is also nothing here to memorize: the axes are cultural rather than factual, so no amount of training data tells a model how a crowd interpreted a prompt. The only way to get it right is for the model to understand how people would think and interpret.
8
8
 
9
- Commonly-cited benchmarks, such as [SWE-bench](https://www.swebench.com/) and [HLE](https://lastexam.ai/), are exam-like: multiple choice, math, and code. Models have been optimized for their shape. But they are very mechanical, and their results are not indicative of a model's ability to "read the room." If the industry ambition is a model that surpasses human intelligence, the comparison has to include the things humans are good at, like having cultural fluency, which models generally lack. That's why Mixed Messages provides a unique and differentiated dataset to benchmark against.
9
+ Commonly-cited benchmarks, such as [SWE-bench](https://www.swebench.com/) and [HLE](https://lastexam.ai/), measure whether a model can produce a correct answer: multiple choice, math, and code. RTR-bench measures a different subject. There is no answer key in this data, only how people actually interpreted an ambiguous prompt, so a model is scored against the crowd rather than against a key. That is what makes it a test of whether a model can read the room.
10
10
 
11
- **RTR-bench**, or read-the-room, is not a knowledge test. That is deliberate. It is an attempt at a different kind of benchmark, built from a creative act rather than an exam structure. Cultural fluency resists the traditional benchmark shape, which is why this one is a prediction task that measures whether a model can anticipate how people would interpret a prompt, and whether it would have interpreted that prompt the same way, both against real human data.
11
+ **RTR-bench** is not a knowledge test, and that is deliberate. It is a prediction task. Given a prompt, a model has to decide how the crowd interpreted it, and then decide how it would interpret it itself. Both answers are scored against the real submissions.
12
12
 
13
13
  ## How it works
14
14
 
@@ -29,28 +29,45 @@ Commonly-cited benchmarks, such as [SWE-bench](https://www.swebench.com/) and [H
29
29
 
30
30
  ## Quickstart
31
31
 
32
- Scoring needs no database access.
32
+ Scoring needs no database access, just an OpenRouter API key.
33
33
 
34
- ```bash
35
- git clone https://github.com/1999labs/RTR-bench
36
- cd RTR-bench
37
- npm install
34
+ ### Test a model
38
35
 
39
- # keep the key in a file, not in shell history
36
+ Node 18 or newer. No clone, no install.
37
+
38
+ ```bash
39
+ mkdir rtr-run && cd rtr-run
40
40
  echo 'OPENROUTER_API_KEY=sk-or-your-key' > .env
41
41
 
42
- # list ids and prices
43
- node src/cli.ts catalog --limit 20
42
+ # see ids and prices. costs nothing
43
+ npx --yes rtr-bench catalog --limit 20
44
44
 
45
- # run a model, then score everything on disk
46
- node src/cli.ts run --models z-ai/glm-5.3-flash --concurrency 8
47
- node src/cli.ts report
45
+ # run one model, then score it
46
+ npx --yes rtr-bench z-ai/glm-5.3-flash
48
47
  ```
49
48
 
50
- Published on npm, the same run is one command with no clone:
49
+ `--yes` skips npx's install prompt. A run makes 1,320 calls, takes about four minutes on a cheap model, costs a few cents, and writes `results/` in the folder you created. Add `--concurrency 8` to go faster.
50
+
51
+ By default OpenRouter may route each call to any host that serves the model, and hosts differ in quantisation. List them, with price and precision:
51
52
 
52
53
  ```bash
53
- npx rtr-bench z-ai/glm-5.3-flash
54
+ npx --yes rtr-bench providers z-ai/glm-5.3-flash
55
+ ```
56
+
57
+ Pin one with `--provider Wafer`. A pinned run refuses to fall back, and the summary names the host that served the calls.
58
+
59
+ ### Work on the code
60
+
61
+ The repository runs the TypeScript sources directly, so this path needs Node 22.18 or newer.
62
+
63
+ ```bash
64
+ git clone https://github.com/1999labs/RTR-bench
65
+ cd RTR-bench
66
+ npm install
67
+ echo 'OPENROUTER_API_KEY=sk-or-your-key' > .env
68
+ node src/cli.ts catalog --limit 20
69
+ node src/cli.ts run --models z-ai/glm-5.3-flash --concurrency 8
70
+ node src/cli.ts report
54
71
  ```
55
72
 
56
73
  Rebuilding the dataset needs the project's database connection, so `npm run export` is included for inspection only.
@@ -59,16 +76,25 @@ Rebuilding the dataset needs the project's database connection, so `npm run expo
59
76
 
60
77
  | Model | Provider | Score | Cost |
61
78
  |---|---|---|---|
62
- | z-ai/glm-5.3-flash | TBD | TBD | TBD |
63
- | z-ai/glm-5.3 | TBD | TBD | TBD |
64
- | minimax/minimax-m3.1-flash | TBD | TBD | TBD |
65
- | deepseek/deepseek-v4.1-flash-1017 | TBD | TBD | TBD |
66
- | tencent/hy4 | TBD | TBD | TBD |
67
- | google/gemini-3.8-flash | TBD | TBD | TBD |
68
- | google/gemini-4-carbon | TBD | TBD | TBD |
69
- | moonshotai/kimi-k3.1-pro | TBD | TBD | TBD |
70
- | x-ai/grok-4.7 | TBD | TBD | TBD |
71
- | qwen/qwen3.8-max-0902 | TBD | TBD | TBD |
79
+ | z-ai/glm-5.3-flash | Tenstorrent (fp8) | 41.2 | $0.0281 |
80
+ | z-ai/glm-5.3 | Tenstorrent (fp8) | 40.0 | $0.2828 |
81
+ | minimax/minimax-m3.1-flash | GMICloud (fp8) | 32.1 | $0.2444 |
82
+ | xiaomi/mimo-v2.6-flash | GMICloud (bf16) | 40.6 | $0.1216 |
83
+ | deepseek/deepseek-v4.1-flash-1017 | Morph (fp8) | 40.0 | $0.3414 |
84
+ | tencent/hy3 | Tencent (fp8) | 40.0 | $0.2665 |
85
+ | google/gemini-3.8-flash | Google AI Studio | 46.1 | $0.7790 |
86
+ | openai/gpt-6-luna | OpenAI | 37.0 | $0.1329 |
87
+ | anthropic/claude-haiku-5.5 | Claude Platform on AWS | 43.0 | $0.1696 |
88
+ | thinkingmachines/inkling-small | DeepInfra (fp8) | 37.0 | $0.1733 |
89
+ | openai/gpt-oss-120b | DekaLLM (bf16) | 32.7 | $0.0205 |
90
+
91
+ ![RTR-bench: score against cost](results/leaderboard-scatter.png)
92
+
93
+ Eleven models were tested against the same 165 windows. Scores concentrated in the high 30s and low 40s. Models were more alike in how they fail than in how they score. Across 1,591 answers, they decided on Honest x Uniting 67.7% of the time and Honest x Dividing 18.2% of the time, while the dominant quadrant was Honest x Dividing in 39 of those 165 windows. Division, when a model registers it at all, reads as spin or as bad faith, and almost never as candor.
94
+
95
+ There were seven prompts that no model got right. They were Donald Trump, Elon Musk, Las Vegas, Barack Obama, Reputation, Dreams, and Honor. The dominant quadrant for each of them besides Dreams was Honest x Dividing. There were three prompts that every model got right. They were Handwritten Notes, The CIA, and Altruism.
96
+
97
+ My main takeaway is that the same blind spot shows up in every model, and it is not a vocabulary problem. Ask it to pick the dominant quadrant for Trump or Musk and the model can easily choose dividing instead of uniting, but has a hard time determining honest or performed. That is why Honest X Dividing is the quadrant the models got wrong the most. Next, I would like to score more closed frontier models, score only the windows where the crowd was very decisive, and somehow factor into the benchmark the conviction and polarization scores from each window.
72
98
 
73
99
  ## License
74
100
 
package/dist/cli.js CHANGED
@@ -120,7 +120,8 @@ function readModel(dir) {
120
120
  order: rec.order,
121
121
  task,
122
122
  parsed: rec.parsed ?? null,
123
- cost: rec.usage?.cost ?? rec.usage?.total_cost ?? 0
123
+ cost: rec.usage?.cost ?? rec.usage?.total_cost ?? 0,
124
+ provider: rec.provider ?? null
124
125
  });
125
126
  }
126
127
  }
@@ -236,6 +237,10 @@ function scoreModel(model, windows) {
236
237
  const mean = (xs) => xs.length ? xs.reduce((a, b) => a + b, 0) / xs.length : NaN;
237
238
  const predictAcc = mean(predHits);
238
239
  const readAgree = mean(readHits);
240
+ const providers = {};
241
+ for (const c of calls) {
242
+ if (c.provider) providers[c.provider] = (providers[c.provider] ?? 0) + 1;
243
+ }
239
244
  return {
240
245
  model: display,
241
246
  windows: predHits.length,
@@ -247,7 +252,8 @@ function scoreModel(model, windows) {
247
252
  gap: readAgree - predictAcc,
248
253
  instability: pred.length ? pred.filter((a) => a.unstable).length / pred.length : NaN,
249
254
  parseFailures: calls.filter((c) => !c.parsed).length,
250
- costUsd: calls.reduce((a, c) => a + c.cost, 0)
255
+ costUsd: calls.reduce((a, c) => a + c.cost, 0),
256
+ providers
251
257
  };
252
258
  }
253
259
  function majorityBaseline(windows) {
@@ -313,19 +319,30 @@ import fs3 from "node:fs";
313
319
  import path2 from "node:path";
314
320
  var API = "https://openrouter.ai/api/v1/chat/completions";
315
321
  var apiKey = () => process.env.OPENROUTER_API_KEY ?? "";
322
+ var nextSlot = 0;
323
+ async function pace(rpm) {
324
+ if (!rpm) return;
325
+ const gap = 6e4 / rpm;
326
+ const now = Date.now();
327
+ const start = Math.max(now, nextSlot);
328
+ nextSlot = start + gap;
329
+ const wait = start - now;
330
+ if (wait > 0) await new Promise((r) => setTimeout(r, wait));
331
+ }
316
332
  function slug(model) {
317
333
  return model.replace(/[^a-zA-Z0-9]+/g, "-").toLowerCase();
318
334
  }
319
- async function call(model, task, word, order) {
335
+ async function call(model, task, word, order, provider) {
320
336
  const messages = buildMessages(task, word, order);
321
337
  const body = {
322
338
  model,
323
339
  messages,
324
340
  temperature: 0,
325
341
  reasoning: { effort: "low" },
326
- max_tokens: 400,
342
+ max_tokens: 2e3,
327
343
  usage: { include: true }
328
344
  };
345
+ if (provider) body.provider = { order: [provider], allow_fallbacks: false };
329
346
  const post = async () => fetch(API, {
330
347
  method: "POST",
331
348
  headers: {
@@ -360,12 +377,32 @@ async function call(model, task, word, order) {
360
377
  const text = await res.text();
361
378
  throw new Error(`HTTP ${status}: ${text.slice(0, 300)}`);
362
379
  }
363
- const j = await res.json();
380
+ const j0 = await res.json();
381
+ let j = j0;
382
+ const noContent = !(j0?.choices?.[0]?.message?.content ?? "") && j0?.choices?.[0]?.finish_reason === "length";
383
+ if (noContent) {
384
+ const bigger = { ...body, max_tokens: 4e3 };
385
+ const r2 = await fetch(API, {
386
+ method: "POST",
387
+ headers: {
388
+ Authorization: `Bearer ${apiKey()}`,
389
+ "Content-Type": "application/json",
390
+ "HTTP-Referer": "https://mixedmessages.fyi",
391
+ "X-Title": "RTR-bench"
392
+ },
393
+ body: JSON.stringify(bigger)
394
+ });
395
+ if (r2.ok) {
396
+ j = await r2.json();
397
+ body.max_tokens = 4e3;
398
+ }
399
+ }
364
400
  return {
365
401
  json: j,
366
402
  body,
367
403
  temperature: "temperature" in body ? 0 : null,
368
- reasoningEffort: body.reasoning?.effort ?? null
404
+ reasoningEffort: body.reasoning?.effort ?? null,
405
+ maxTokens: body.max_tokens
369
406
  };
370
407
  }
371
408
  async function withRetry(fn, tries = 4) {
@@ -388,11 +425,31 @@ async function runModel(windows, opts) {
388
425
  throw new Error(
389
426
  "OPENROUTER_API_KEY is not set. Put it in .env in this directory, or export it in your shell."
390
427
  );
391
- const dir = path2.join("results", "raw", slug(opts.model));
428
+ try {
429
+ const res = await fetch(`${API.replace("/chat/completions", "")}/credits`, {
430
+ headers: { Authorization: `Bearer ${apiKey()}` }
431
+ });
432
+ if (res.ok) {
433
+ const d = (await res.json()).data;
434
+ const remaining = d.total_credits - d.total_usage;
435
+ if (remaining <= 0)
436
+ throw new Error(
437
+ `OpenRouter balance is $${remaining.toFixed(4)}. Add credit at https://openrouter.ai/settings/credits, then rerun.`
438
+ );
439
+ }
440
+ } catch (e) {
441
+ if (String(e.message).startsWith("OpenRouter balance")) throw e;
442
+ }
443
+ const dir = path2.join(
444
+ "results",
445
+ "raw",
446
+ slug(opts.model) + (opts.provider ? `@${slug(opts.provider)}` : "")
447
+ );
392
448
  let done = 0;
393
449
  let failed = 0;
394
450
  let tokens = 0;
395
451
  let usd = 0;
452
+ const served = {};
396
453
  const list = opts.limit ? windows.slice(0, opts.limit) : windows;
397
454
  const jobs = [];
398
455
  for (const w of list) {
@@ -403,7 +460,17 @@ async function runModel(windows, opts) {
403
460
  }
404
461
  }
405
462
  const file2 = (j) => path2.join(dir, j.task, `${j.day}-${j.order}.json`);
406
- const queue = jobs.filter((j) => !fs3.existsSync(file2(j)));
463
+ const want = opts.provider ?? null;
464
+ const isDone = (f) => {
465
+ if (!fs3.existsSync(f)) return false;
466
+ try {
467
+ const rec = JSON.parse(fs3.readFileSync(f, "utf8"));
468
+ return !rec.error && rec.parsed && (rec.providerRequested ?? null) === want;
469
+ } catch {
470
+ return false;
471
+ }
472
+ };
473
+ const queue = jobs.filter((j) => !isDone(file2(j)));
407
474
  console.log(
408
475
  `${opts.model}: ${jobs.length} calls, ${jobs.length - queue.length} already on disk, ${queue.length} to go`
409
476
  );
@@ -415,13 +482,15 @@ async function runModel(windows, opts) {
415
482
  fs3.mkdirSync(path2.dirname(target), { recursive: true });
416
483
  let rec;
417
484
  try {
418
- const { json, body, temperature, reasoningEffort } = await withRetry(
419
- () => call(opts.model, j.task, j.word, j.order)
485
+ await pace(opts.rpm);
486
+ const { json, body, temperature, reasoningEffort, maxTokens } = await withRetry(
487
+ () => call(opts.model, j.task, j.word, j.order, opts.provider)
420
488
  );
421
489
  const raw = json?.choices?.[0]?.message?.content ?? "";
422
490
  const usage = json?.usage ?? {};
423
491
  tokens += usage.total_tokens ?? 0;
424
- usd += Number(json?.usage?.total_cost ?? 0);
492
+ usd += Number(usage.cost ?? usage.total_cost ?? 0);
493
+ if (json?.provider) served[json.provider] = (served[json.provider] ?? 0) + 1;
425
494
  rec = {
426
495
  model: opts.model,
427
496
  day: j.day,
@@ -431,6 +500,8 @@ async function runModel(windows, opts) {
431
500
  promptHash: PROMPT_HASH,
432
501
  temperature,
433
502
  reasoningEffort,
503
+ providerRequested: opts.provider ?? null,
504
+ maxTokens,
434
505
  request: body,
435
506
  raw,
436
507
  parsed: parseAnswer(raw),
@@ -450,6 +521,8 @@ async function runModel(windows, opts) {
450
521
  promptHash: PROMPT_HASH,
451
522
  temperature: 0,
452
523
  reasoningEffort: null,
524
+ providerRequested: opts.provider ?? null,
525
+ maxTokens: null,
453
526
  request: null,
454
527
  raw: "",
455
528
  parsed: null,
@@ -470,8 +543,29 @@ async function runModel(windows, opts) {
470
543
  }
471
544
  };
472
545
  await Promise.all(Array.from({ length: opts.concurrency }, worker));
546
+ const names = Object.keys(served);
547
+ const mix = names.length ? names.map((n) => `${n} ${served[n]}`).join(", ") : "unknown";
473
548
  console.log(`${opts.model}: wrote ${done}, failed ${failed}, tokens ${tokens.toLocaleString()}`);
474
- return { done, failed, tokens, usd };
549
+ console.log(`${opts.model}: providers ${mix}${opts.provider ? " (pinned)" : ""}`);
550
+ return { done, failed, tokens, usd, providers: served };
551
+ }
552
+ async function listEndpoints(model) {
553
+ const res = await fetch(`${API.replace("/chat/completions", "")}/models/${model}/endpoints`, {
554
+ headers: { "User-Agent": "RTR-bench", Accept: "application/json" }
555
+ });
556
+ if (!res.ok) throw new Error(`endpoints HTTP ${res.status}`);
557
+ const j = await res.json();
558
+ const eps = j.data?.endpoints ?? [];
559
+ return eps.map((e) => ({
560
+ provider: e.provider ?? e.provider_name ?? "?",
561
+ quant: e.quantization ?? "?",
562
+ inUsd: +(Number(e.pricing?.prompt) * 1e6).toFixed(3),
563
+ outUsd: +(Number(e.pricing?.completion) * 1e6).toFixed(3),
564
+ ctx: e.context_length ?? null,
565
+ status: e.status ?? null
566
+ })).sort(
567
+ (a, b) => (a.status === 0 ? 0 : 1) - (b.status === 0 ? 0 : 1) || a.inUsd - b.inUsd
568
+ );
475
569
  }
476
570
  async function listModels() {
477
571
  const res = await fetch("https://openrouter.ai/api/v1/models", {
@@ -533,12 +627,12 @@ function writeReport() {
533
627
  lines.push("## Leaderboard");
534
628
  lines.push("");
535
629
  lines.push(
536
- "| model | score /100 | n | cost | 95% CI | axis x | axis y | Brier | own reading agrees | crowd distance | gap | unstable |"
630
+ "| model | score /100 | n | cost | provider | 95% CI | axis x | axis y | Brier | own reading agrees | crowd distance | gap | unstable |"
537
631
  );
538
- lines.push("|---|---|---|---|---|---|---|---|---|---|---|---|");
632
+ lines.push("|---|---|---|---|---|---|---|---|---|---|---|---|---|");
539
633
  for (const s of ranked) {
540
634
  lines.push(
541
- `| \`${s.model}\` | **${(100 * s.predict.acc).toFixed(1)}** | ${s.windows} | $${s.costUsd.toFixed(4)} | ${ci(s.predict.ci)} | ${pct(s.axes.x)} | ${pct(s.axes.y)} | ${round(s.brier)} | ${pct(s.read.agree)} | ${round(s.crowdDistance)} | ${s.gap >= 0 ? "+" : ""}${pct(s.gap)} | ${pct(s.instability)} |`
635
+ `| \`${s.model}\` | **${(100 * s.predict.acc).toFixed(1)}** | ${s.windows} | $${s.costUsd.toFixed(4)} | ${providerLabel(s)} | ${ci(s.predict.ci)} | ${pct(s.axes.x)} | ${pct(s.axes.y)} | ${round(s.brier)} | ${pct(s.read.agree)} | ${round(s.crowdDistance)} | ${s.gap >= 0 ? "+" : ""}${pct(s.gap)} | ${pct(s.instability)} |`
542
636
  );
543
637
  }
544
638
  lines.push("");
@@ -611,6 +705,12 @@ function writeReport() {
611
705
  console.log("wrote results/leaderboard.md and results/leaderboard.svg");
612
706
  return data;
613
707
  }
708
+ function providerLabel(s) {
709
+ const names = Object.keys(s.providers);
710
+ if (names.length === 0) return "?";
711
+ if (names.length === 1) return names[0];
712
+ return `${names.length} providers`;
713
+ }
614
714
  function scoreLine(s) {
615
715
  const [lo, hi] = s.predict.ci;
616
716
  return [
@@ -618,7 +718,8 @@ function scoreLine(s) {
618
718
  `${(100 * s.predict.acc).toFixed(1)} / 100`,
619
719
  `${s.windows} windows`,
620
720
  `95% CI ${(100 * lo).toFixed(1)} to ${(100 * hi).toFixed(1)}`,
621
- `cost $${s.costUsd.toFixed(4)}`
721
+ `cost $${s.costUsd.toFixed(4)}`,
722
+ `provider ${providerLabel(s)}`
622
723
  ].join(" ");
623
724
  }
624
725
  function svg(scores, baselines, windowCount) {
@@ -719,8 +820,10 @@ if (cmd === "catalog") {
719
820
  const windows = loadWindows().map((w) => ({ day: w.day, word: w.word }));
720
821
  const limit = flags.limit ? Number(flags.limit) : void 0;
721
822
  const concurrency = Number(flags.concurrency ?? 6);
823
+ const rpm = flags.rpm ? Number(flags.rpm) : void 0;
824
+ const provider = flags.provider ? String(flags.provider) : void 0;
722
825
  for (const model of models) {
723
- await runModel(windows, { model, tasks: [...tasks], concurrency, limit });
826
+ await runModel(windows, { model, tasks: [...tasks], concurrency, limit, provider, rpm });
724
827
  }
725
828
  const data = writeReport();
726
829
  for (const s of data.scores) {
@@ -744,11 +847,16 @@ if (cmd === "catalog") {
744
847
  model: cmd,
745
848
  tasks: ["predict", "read"],
746
849
  concurrency: Number(flags.concurrency ?? 6),
747
- limit: flags.limit ? Number(flags.limit) : void 0
850
+ limit: flags.limit ? Number(flags.limit) : void 0,
851
+ provider: flags.provider ? String(flags.provider) : void 0
748
852
  });
749
853
  const data = writeReport();
750
854
  const s = data.scores.find((x) => x.model === cmd);
751
855
  if (s) console.log(scoreLine(s));
856
+ } else if (cmd === "providers") {
857
+ const id = rest.find((a) => !a.startsWith("--"));
858
+ if (!id) throw new Error("usage: rtr-bench providers <model-id>");
859
+ console.table(await listEndpoints(id));
752
860
  } else {
753
861
  const here = path4.dirname(fileURLToPath2(import.meta.url));
754
862
  console.log(fs5.readFileSync(path4.join(here, "..", "README.md"), "utf8").slice(0, 1200));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rtr-bench",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Can a language model predict how a crowd reads a word? A cultural fluency benchmark built from Mixed Messages.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -25,6 +25,7 @@
25
25
  "files": [
26
26
  "dist/",
27
27
  "data/",
28
+ "results/leaderboard-scatter.png",
28
29
  "README.md",
29
30
  "LICENSE"
30
31
  ],
Binary file