-
Notifications
You must be signed in to change notification settings - Fork 0
/
script.js
110 lines (98 loc) · 3.68 KB
/
script.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import { num, num0 } from "https://cdn.jsdelivr.net/npm/@gramex/[email protected]/dist/format.js";
import { marked } from "https://cdn.jsdelivr.net/npm/marked@12/+esm";
import * as Plot from "https://cdn.jsdelivr.net/npm/@observablehq/[email protected]/+esm";
import * as d3 from "https://cdn.jsdelivr.net/npm/[email protected]/+esm";
import { default as fuzzysort } from "https://cdn.jsdelivr.net/npm/fuzzysort@3/+esm";
// Load and display README content
const content = await fetch("README.md").then((r) => r.text());
document.querySelector("#README").innerHTML = marked.parse(content);
let quality = new URLSearchParams(window.location.search).get("quality") || "overall";
document.querySelector("#quality").textContent = quality.charAt(0).toUpperCase() + quality.slice(1);
// Load and process model data
const data = await d3.csv("elo.csv");
const models = data.filter((d) => d.cpmi).map((d) => ({ ...d, cost: +d.cpmi, elo: +d[quality] }));
const dates = Array.from(new Set(models.map((d) => d.launch))).sort();
const $date = document.querySelector("#date");
$date.setAttribute("max", dates.length - 1);
$date.value = dates.length - 1;
const xScale = d3
.scaleLog()
.domain(d3.extent(models, (d) => d.cost))
.range([0, 1000]);
const yScale = d3
.scaleLinear()
.domain(d3.extent(models, (d) => d.elo))
.range([500, 0]);
const updateOptimalStatus = (filteredModels) => {
filteredModels.forEach((model) => {
model.optimal = filteredModels.every((other) => other === model || other.elo < model.elo || other.cost > model.cost)
? "best"
: filteredModels.every((other) => other === model || other.elo >= model.elo || other.cost <= model.cost)
? "worst"
: "";
});
};
const renderPlot = (filteredModels) => {
const plot = Plot.plot({
marginLeft: 50,
x: { type: "log", grid: true, domain: xScale.domain() },
y: { grid: true, domain: yScale.domain() },
width: 1000,
height: 500,
marks: [
Plot.dot(filteredModels, {
x: "cost",
y: "elo",
r: 8,
fill: (d) =>
d.optimal === "best" ? "lime" : d.optimal === "worst" ? "red" : "rgba(var(--bs-body-color-rgb), 0.1)",
stroke: "black",
strokeWidth: 0.5,
channels: { model: "model" },
tip: {
fill: "var(--bs-body-bg)",
format: {
fill: false,
x: (d) => `$${num(d)} / MTok`,
y: (d) => num0(d),
},
},
}),
Plot.text(
filteredModels.filter((d) => d.optimal),
{
x: "cost",
y: "elo",
text: (d) => d.model,
dy: -10,
lineAnchor: "bottom",
}
),
Plot.axisX({ label: "Cost per million input tokens" }),
Plot.axisY({ label: "ELO score", tickSpacing: 100 }),
],
});
document.querySelector("#llm-cost").replaceChildren(plot);
// Add nodes to models for search functionality
const circles = document.querySelectorAll("#llm-cost circle");
models.forEach((model, i) => (model.node = circles[i]));
};
const update = () => {
const date = dates[$date.value];
document.querySelector("#date-label").textContent = d3.timeFormat("%b %Y")(d3.timeParse("%Y-%m")(date));
const search = document.querySelector("#model").value.trim();
const results = fuzzysort.go(
search,
models.map((m) => m.model),
{ threshold: -20 }
);
const matches = new Set(results.map((r) => r.target));
const filteredModels = models.filter(
(d) => d.launch <= date && (d.end ? d.end > date : true) && (search ? matches.has(d.model) : true)
);
updateOptimalStatus(filteredModels);
renderPlot(filteredModels);
};
$date.addEventListener("input", update);
document.querySelector("#model").addEventListener("input", update);
update();