Skip to content

Commit b030657

Browse files
committed
site: contributors column, star refresh, and two chart fixes
- Roster: replace the near-constant Class column with a Contributors column that credits outside submitters/maintainers (@moonming on apisix/aisix, @geekspeng on higress once it lands). Data lives in a SITE-ONLY site/contributors.json, never in definition.json: a manifest schema change would perturb the frozen ENGINE_PIN and force a full 14-gateway re-run. This does not. - Charts: cost-per-million no longer renders a below_resolution cell (no measurable throughput -> undefined cost) as $0.0000, which read as the cheapest gateway on the board; it now drops out honestly like any unmeasured cell. - Charts: Peak memory metric called an undefined chooserCellMemory() and threw, so the chart never updated on selection; point it at memoryFor(), the reader every other memory surface uses. - Refresh the committed GitHub star snapshot for all 14 gateways.
1 parent 189c202 commit b030657

6 files changed

Lines changed: 93 additions & 34 deletions

File tree

‎gateways/stars.json‎

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,72 @@
11
{
22
"agentgateway": {
3-
"stars": 4466,
3+
"stars": 4556,
44
"first_commit": "2025-03-18",
5-
"as_of": "2026-08-21"
5+
"as_of": "2026-08-26"
66
},
77
"aisix": {
8-
"stars": 114,
8+
"stars": 121,
99
"first_commit": "2026-04-17",
10-
"as_of": "2026-08-21"
10+
"as_of": "2026-08-26"
1111
},
1212
"apisix": {
13-
"stars": 17016,
13+
"stars": 17037,
1414
"first_commit": "2019-04-10",
15-
"as_of": "2026-08-21"
15+
"as_of": "2026-08-26"
1616
},
1717
"bifrost": {
18-
"stars": 7483,
18+
"stars": 7583,
1919
"first_commit": "2025-03-19",
20-
"as_of": "2026-08-21"
20+
"as_of": "2026-08-26"
2121
},
22-
"busbar": {
23-
"stars": 118,
22+
"busbar-151": {
23+
"stars": 116,
2424
"first_commit": "2026-05-29",
25-
"as_of": "2026-08-21"
25+
"as_of": "2026-08-26"
2626
},
2727
"gomodel": {
28-
"stars": 1073,
28+
"stars": 1082,
2929
"first_commit": "2025-12-13",
30-
"as_of": "2026-08-21"
30+
"as_of": "2026-08-26"
3131
},
3232
"helicone": {
3333
"stars": 624,
3434
"first_commit": "2025-04-01",
35-
"as_of": "2026-08-21"
35+
"as_of": "2026-08-26"
3636
},
3737
"kong": {
38-
"stars": 44017,
38+
"stars": 44043,
3939
"first_commit": "2014-11-17",
40-
"as_of": "2026-08-21"
40+
"as_of": "2026-08-26"
4141
},
4242
"litellm-python": {
43-
"stars": 56946,
43+
"stars": 57322,
4444
"first_commit": "2023-07-27",
45-
"as_of": "2026-08-21"
45+
"as_of": "2026-08-26"
4646
},
4747
"litellm-rust": {
48-
"stars": 56946,
48+
"stars": 57322,
4949
"first_commit": "2023-07-27",
50-
"as_of": "2026-08-21"
50+
"as_of": "2026-08-26"
5151
},
5252
"one-api": {
53-
"stars": 36517,
53+
"stars": 36590,
5454
"first_commit": "2023-04-22",
55-
"as_of": "2026-08-21"
55+
"as_of": "2026-08-26"
5656
},
5757
"plano": {
58-
"stars": 7013,
58+
"stars": 7016,
5959
"first_commit": "2024-07-09",
60-
"as_of": "2026-08-21"
60+
"as_of": "2026-08-26"
6161
},
6262
"portkey": {
63-
"stars": 12790,
63+
"stars": 12837,
6464
"first_commit": "2023-08-23",
65-
"as_of": "2026-08-21"
65+
"as_of": "2026-08-26"
6666
},
6767
"tensorzero": {
68-
"stars": 11721,
68+
"stars": 11719,
6969
"first_commit": "2024-07-16",
70-
"as_of": "2026-08-21"
70+
"as_of": "2026-08-26"
7171
}
7272
}

‎site/app.js‎

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,7 +1202,13 @@ const CHART_METRICS = [
12021202
get: (g, st) => mval((chooserCellPerf(g, st) || {}).rps_per_dollar) },
12031203
{ id: "permillion", label: "Cost per million requests", unit: "USD", log: true, desc: false,
12041204
note: "Instance cost to serve a million requests at the selected bound. Lower is better.",
1205-
get: (g, st) => mval((chooserCellPerf(g, st) || {}).cost_per_million_usd) },
1205+
// Cost is instance $/hr ÷ throughput, so a below_resolution cell (no rate the rig could weigh)
1206+
// makes cost UNDEFINED, not zero: coercing mval's below_resolution→0 here renders $0.0000, which
1207+
// reads as the CHEAPEST gateway on the board when in truth it sustained no measurable throughput -
1208+
// the exact inverse of the metric. Drop it like any unmeasured cell (it shows in the "not shown"
1209+
// note) instead. This is why cost cannot share mval's magnitude coercion with added-latency/CPU.
1210+
get: (g, st) => { const e = (chooserCellPerf(g, st) || {}).cost_per_million_usd;
1211+
return (isEnvelope(e) && e.reason === "below_resolution") ? null : mval(e); } },
12061212
{ id: "lat", label: "Added latency (p99)", unit: "µs", log: true, desc: false,
12071213
note: "Gateway p99 minus direct-to-mock p99 at concurrency 1. Lower is better.",
12081214
get: (g, st) => mval((chooserCellPerf(g, st) || {}).added_latency_p99_us) },
@@ -1211,7 +1217,7 @@ const CHART_METRICS = [
12111217
get: (g, st) => { const r = frontierAt(frontierOf(chooserCellPerf(g, st)), selectedBound(st)); return r ? mval(r.rps) : null; } },
12121218
{ id: "rss", label: "Peak memory", unit: "MiB", log: false, desc: false,
12131219
note: "Highest resident memory observed while the fixed load ran on the chosen cell. Lower is better.",
1214-
get: (g, st) => mval((chooserCellMemory(g, st) || {}).peak_rss_mib) },
1220+
get: (g, st) => mval((memoryFor(g, st) || {}).peak_rss_mib) },
12151221
];
12161222

12171223
/* LOG SCALE IS NOT A PREFERENCE ON SOME OF THESE, IT IS THE ONLY HONEST AXIS.
@@ -4838,7 +4844,9 @@ const ROSTER_KEY = {
48384844
lastrun: (g) => { const d = gatewayLastRun(g); return d ? d.getTime() : null; }, // newer = larger ms
48394845
age: (g) => (g.first_commit ? new Date(g.first_commit).getTime() : null), // older = smaller ms
48404846
stars: (g) => (g.stars == null ? null : g.stars),
4841-
cls: (g) => (g.cls || "Gateway").toLowerCase(),
4847+
// First-party rows (no contributor) sort LAST in either direction, so the outside contributors -
4848+
// the whole point of the column - cluster at the top when a reader sorts by it.
4849+
contrib: (g) => { const cs = g.contributed_by || []; return cs.length ? cs[0].handle.toLowerCase() : null; },
48424850
};
48434851
const rosterRows = (gateways) => {
48444852
const key = ROSTER_KEY[rosterSort.col] || ROSTER_KEY.name;
@@ -4858,6 +4866,22 @@ const rosterRows = (gateways) => {
48584866
/* Star counts render compact: 12345 -> "12.3k", below 1000 the full int. Null (no
48594867
snapshot entry) stays null; the cell renders it muted. */
48604868
const fmtStars = (v) => (v == null ? null : v >= 1000 ? `${(v / 1000).toFixed(1)}k` : String(Math.round(v)));
4869+
/* The Contributors cell: the outside people who submitted or maintain this entrant, each a
4870+
`@handle` chip linking to their GitHub profile (url pre-validated to https-only in gen-data, so
4871+
it is safe raw in href; a null url renders the chip as plain text). A first-party entrant has no
4872+
contributor and renders a muted dash - the column exists to show that several rows do, so the
4873+
bench is visibly not all first-party. */
4874+
function contribCell(g) {
4875+
const cs = g.contributed_by || [];
4876+
if (!cs.length) return `<span class="muted" title="first-party entrant">&mdash;</span>`;
4877+
return cs.map((c) => {
4878+
const chip = `@${esc(c.handle)}`;
4879+
const title = esc(`${c.name || c.handle} contributed this entrant`);
4880+
return c.url
4881+
? `<a class="contrib-link" href="${c.url}" target="_blank" rel="noopener noreferrer" title="${title}">${chip}</a>`
4882+
: `<span class="contrib-link" title="${title}">${chip}</span>`;
4883+
}).join(" ");
4884+
}
48614885
/* Project age from the repo's first-commit date, in ONE simple floored unit: "11+ years",
48624886
"7+ months", "3+ weeks". Context for the star counts and scores - a decade-old project and a
48634887
three-week-old one earn them differently. Null (no snapshot) renders muted. */
@@ -5055,7 +5079,7 @@ function renderGateways() {
50555079
<td class="lastrun">${lastRunTxt ? `${runModeCell(g)}<span title="last benchmarked ${esc(lastRun.toISOString().slice(0, 16).replace("T", " "))} UTC">${esc(lastRunTxt)}</span>` : `<span class="muted">n/a</span>`}</td>
50565080
<td class="age">${age ? `<span title="first commit ${esc(g.first_commit)}">${esc(age)}</span>` : `<span class="muted">n/a</span>`}</td>
50575081
<td class="stars">${stars != null ? esc(stars) : `<span class="muted">n/a</span>`}</td>
5058-
<td class="cls">${esc(g.cls || "Gateway")}</td>
5082+
<td class="contrib">${contribCell(g)}</td>
50595083
</tr>`;
50605084
}).join("");
50615085
// Row click opens the per-gateway drawer (same as the perf tabs) — /gateways rows are clickable too.

‎site/contributors.json‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"apisix": [
3+
{ "handle": "moonming", "name": "Ming Wen", "url": "https://github.com/moonming" }
4+
],
5+
"aisix": [
6+
{ "handle": "moonming", "name": "Ming Wen", "url": "https://github.com/moonming" }
7+
],
8+
"higress": [
9+
{ "handle": "geekspeng", "name": "geekspeng", "url": "https://github.com/geekspeng" }
10+
]
11+
}

‎site/gen-data.mjs‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,13 @@ function resolvedSnapshot(key) {
272272
// absent key degrades to null; the site renders those muted.
273273
const starsSnap = readJson(join(gatewaysDir, "stars.json")) || {};
274274

275+
// OUTSIDE CONTRIBUTORS, keyed by gateway. Editorial credit, kept in a SITE-ONLY file the engine
276+
// never parses - a contributor field in definition.json would change the engine's Manifest schema,
277+
// and the engine is a frozen instrument (ENGINE_PIN): a schema change there is a board-wide re-run.
278+
// This file is not the instrument, so crediting @moonming and @geekspeng costs no re-measurement.
279+
// Shape: { "<gatewayKey>": [ { "handle": "...", "name": "...", "url": "https://..." }, ... ] }.
280+
const contributorsBook = readJson(join(HERE, "contributors.json")) || {};
281+
275282
// snapshotDefinitionsByKey: gateway key -> the metric-definitions map off the SAME snapshot that
276283
// became that gateway's g.matrix (see the capture below). Kept OUT of the gateway object itself -
277284
// the per-gateway records are the public bundle shape and definitions are a board-level projection
@@ -284,6 +291,17 @@ const allGateways = gatewayKeys.map((key) => {
284291
display: meta.display || key,
285292
lang: meta.lang || "Other",
286293
cls: meta.cls || "Gateway",
294+
// Outside contributors (from the site-only contributorsBook), sanitised the same way g.repo is:
295+
// app.js renders handle/name esc()'d but interpolates url RAW into an href, so only https://
296+
// profile URLs survive - a bad/missing url drops to null and the cell renders the handle as
297+
// plain text rather than a link.
298+
contributed_by: (Array.isArray(contributorsBook[key]) ? contributorsBook[key] : [])
299+
.filter((c) => c && typeof c.handle === "string" && c.handle.trim())
300+
.map((c) => ({
301+
handle: c.handle,
302+
name: (typeof c.name === "string" && c.name.trim()) ? c.name : c.handle,
303+
url: (typeof c.url === "string" && /^https:\/\/[^\s"'<>]+$/.test(c.url)) ? c.url : null,
304+
})),
287305
// Only accept an https:// repo URL. app.js interpolates g.repo RAW into href="${g.repo}" at four
288306
// render sites (display is esc()'d, href is not), so a manifest GW_REPO like
289307
// `x" onfocus=alert(...) autofocus="` or a `javascript:` scheme would inject on the public board.

‎site/index.html‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ <h2 class="hero-word">Reproducible, honest benchmarks for the AI tooling you act
101101
<p class="fineprint">Every gateway on the board, in alphabetical order. Language, the exact version measured (hover for the full build/image digest), when it was last benchmarked, project age, stars (snapshot), and each project's own self-description. The other tabs measure how they perform.</p>
102102
<div class="table-scroll">
103103
<table id="gateways-table">
104-
<thead><tr><th data-sort="name">Gateway</th><th data-sort="lang">Language</th><th class="build" data-sort="version">Version</th><th class="lastrun" data-sort="lastrun">Last benchmarked</th><th class="age" data-sort="age">Project Age</th><th class="stars" data-sort="stars">Stars</th><th data-sort="cls">Class</th></tr></thead>
104+
<thead><tr><th data-sort="name">Gateway</th><th data-sort="lang">Language</th><th class="build" data-sort="version">Version</th><th class="lastrun" data-sort="lastrun">Last benchmarked</th><th class="age" data-sort="age">Project Age</th><th class="stars" data-sort="stars">Stars</th><th class="contrib" data-sort="contrib">Contributors</th></tr></thead>
105105
<tbody></tbody>
106106
</table>
107107
</div>

‎site/style.css‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,13 @@ tbody tr { cursor: pointer; }
307307
#gateways-table thead th[data-dir="asc"]::after { content: " \25B2"; font-size: .7em; color: var(--fg-mute); }
308308
#gateways-table thead th[data-dir="desc"]::after { content: " \25BC"; font-size: .7em; color: var(--fg-mute); }
309309
#gateways-table tbody tr { cursor: pointer; }
310-
#gateways-table td.cls { color: var(--fg-dim); }
310+
/* Contributors cell: outside people who submitted/maintain an entrant. A first-party row renders a
311+
muted dash; a credited row shows @handle chips linking to GitHub. The link colour is the normal
312+
accent so it reads as "click me", and it stops the row-click so the profile opens instead of the
313+
drawer. */
314+
#gateways-table td.contrib { color: var(--fg-dim); white-space: nowrap; }
315+
#gateways-table td.contrib .contrib-link { color: var(--accent); text-decoration: none; }
316+
#gateways-table td.contrib a.contrib-link:hover { text-decoration: underline; }
311317

312318
/* drawer (per-gateway slide-over) */
313319
.backdrop { position: fixed; inset: 0; background: var(--overlay); z-index: 60; }

0 commit comments

Comments
 (0)