Skip to content

Commit cf4bb9f

Browse files
author
Wenyao Gao
committed
Merge origin/master into feat/zh-agentx-family, resolve conflict in dataset-detail.tsx
2 parents 05dac0b + 86daa00 commit cf4bb9f

206 files changed

Lines changed: 7387 additions & 1755 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.claude/skills/write-inferencex-blog/iso_interactivity.py‎

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@
2424
Two independent splines need not preserve `metric * throughput = constant` between
2525
measured knots. Pass `reciprocal_of='throughput'` (or the matching output/input
2626
throughput key) to spline that throughput and re-derive the metric. The live
27-
inference charts also expose tokens/$ as a separate metric; that purchasing-power
28-
metric is directly proportional to throughput. Pass `proportional_to='throughput'`
29-
(or the matching output/input throughput key) so it uses the same Pareto knots and
30-
spline as that throughput before applying the recovered multiplier. See
31-
docs/tco-calculator.md for a reproducible measurement.
27+
inference charts also expose tokens/$ and normalized token revenue as separate
28+
metrics; both are directly proportional to throughput. OpenRouter-priced revenue
29+
is proportional only when the input/output mix stays constant; otherwise spline
30+
the already-priced metric directly. Pass
31+
`proportional_to='throughput'` (or the matching output/input throughput key) so
32+
they use the same Pareto knots and spline as that throughput before applying the
33+
recovered multiplier. See docs/tco-calculator.md for a reproducible measurement.
3234
3335
Usage as a module:
3436
from iso_interactivity import interpolate_metric, pareto_front_upper_left
@@ -221,9 +223,11 @@ def interpolate_metric(
221223
the chart code does not extrapolate. Blog tables should render this as
222224
`_unreachable_` in that row.
223225
224-
The Pareto frontier is built on (interactivity, throughput). For tokens/$,
225-
`proportional_to` names the total/output/input throughput it scales; that
226-
throughput also supplies the frontier knots so both curves stay aligned.
226+
The Pareto frontier is built on (interactivity, throughput). For tokens/$ or
227+
normalized token revenue (and revenue with a constant token mix),
228+
`proportional_to` names the total/output/input
229+
throughput it scales; that throughput also supplies the frontier knots so
230+
both curves stay aligned.
227231
228232
`reciprocal_of` names the throughput key a metric divides, for metrics of the
229233
form `constant / throughput` ($/M tok, J/token). Set it for those metrics:
@@ -232,8 +236,10 @@ def interpolate_metric(
232236
latency, and measured energy — whose numerator varies per point).
233237
234238
`proportional_to` names the throughput key for metrics of the form
235-
`throughput * constant` (tokens/$). That throughput is splined and the
236-
recovered multiplier applied, matching the dashboard.
239+
`throughput * constant` (tokens/$, normalized token revenue, and revenue
240+
with a constant input/output mix). That
241+
throughput is splined and the recovered multiplier applied, matching the
242+
dashboard.
237243
"""
238244
if not points:
239245
return None
@@ -318,7 +324,7 @@ def interpolate_metric(
318324
def _cli() -> None:
319325
"""Stdin: {"points": [...], "target_iv": N, "metric_key": "...",
320326
"reciprocal_of": "throughput" for $/M tok and J/token,
321-
"proportional_to": "throughput" for tokens/$}
327+
"proportional_to": "throughput" for tokens/$ or revenue with a constant mix}
322328
Stdout: {"value": N or null}"""
323329
req = json.loads(sys.stdin.read())
324330
value = interpolate_metric(

‎.github/workflows/lint.yml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,5 @@ jobs:
4747
run: bun run lint
4848
- name: Format check
4949
run: bun run fmt
50+
- name: Typography gate
51+
run: bun run check:typography

‎AGENTS.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ Static content routes (no DB):
127127
- **Linter**: oxlint — `bun run lint` / `bun run lint:fix`
128128
- **Formatter**: oxfmt — `bun run fmt` / `bun run fmt:fix`
129129
- **Type checking**: `bun run typecheck` (tsc --noEmit, strict mode)
130+
- **Typography gate**: `bun run check:typography` (CI + pre-commit) — no new arbitrary font sizes (`text-[11px]`) or letter-spacing (`tracking-[0.16em]`) in class strings, and no quoted font-size literals in `src/lib/d3-chart/`. Use `text-2xs`/`text-3xs`, `tracking-eyebrow`/`tracking-eyebrow-wide`/`tracking-heading`, the `<Heading>`/`<Eyebrow>` components, or `CHART_TYPE` from `@/lib/d3-chart/typography` (chart font sizes must stay in TS — CSS variables don't survive PNG export). Existing offenders live in `packages/app/scripts/typography-allowlist.json` and migrate on touch. See [Typography](./docs/typography.md).
130131
- **Node**: 24.x
131132

132133
## Environment Variables
@@ -225,6 +226,7 @@ Authoritative total / active parameter counts for every model in the dashboard.
225226
| Kimi-K2.6 | 1T | 32B | `moonshotai/Kimi-K2.6` | HF model card |
226227
| Kimi-K2.7-Code | 1T | 32B | `moonshotai/Kimi-K2.7-Code` | HF model card |
227228
| Qwen3.5-397B-A17B | 397B | 17B | `Qwen/Qwen3.5-397B-A17B` | HF model card |
229+
| Qwen3.8-Flash-Next | 176B | 6B | `Qwen/Qwen3.8-Flash-Next-FP8` | HF model card |
228230
| GLM-5 | 744B | 40B | `zai-org/GLM-5` | HF model card |
229231
| GLM-5.1 | 744B | 40B | `zai-org/GLM-5.1-FP8` | HF model card (same base as GLM-5) |
230232
| MiniMax-M2.5 | 230B | 10B | `MiniMaxAI/MiniMax-M2.5` | HF model card |
@@ -234,6 +236,7 @@ Authoritative total / active parameter counts for every model in the dashboard.
234236

235237
**Common mislabel traps** (have all bitten this repo at least once — do not repeat):
236238

239+
- **Qwen3.8-Flash-Next is 176B, not 125B.** The model card leads with "125B with 6B activated", but that is the main model only; the 51B n-gram embedding table brings the total to 176B. The separate 4B MTP head sits outside both figures. It is a Qwen4-architecture preview (GatedDeltaNet + Qwen Sparse Attention, 512 experts, 10 routed + 1 shared), not a Qwen3.5 point release, so it gets its own DB bucket.
237240
- **GLM-5 ≠ 355B.** 355B is GLM-4.5. GLM-5 jumped to 744B / 40B active (256-expert MoE with DSA).
238241
- **MiniMax-M2.5/M2.7 ≠ 456B.** 456B is the older MiniMax-Text-01 / M1 (32 large experts). The M2 series is a different architecture: 230B / 10B active, 256 small experts.
239242
- **DeepSeek-R1 is 671B, not 685B.** HF metadata shows 685B because the bundled MTP head adds ~14B; the core MoE is 671B / 37B active.

‎docs/collectivex.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ visible legend labels while retaining them in the legend item's accessible title
9595
The newest run with measured cases is checked by default; newer incomplete sweeps remain listed but
9696
cannot blank the initial explorer. Deletion is available per row or as one confirmed action for all
9797
currently shown runs; both paths keep the same tombstone semantics described above.
98+
The Runs card's suite filter narrows the table to runs containing EP or KV cases; a run containing
99+
both appears under both filters, and changing the filter does not alter the checked-run selection.
98100

99101
## The raw-rows exception
100102

‎docs/data-transforms.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ Returns `{ chartData: InferenceData[][], hardwareConfig: HardwareConfig }`.
6060
**`buildDerivedChartFields(entry, hwKey, requestedMetrics?)`** is the sole owner of inference and historical trend formulas. The full transform requests every benchmark-backed field. History requests only the selected metric and its interpolation dependencies, so it stays lightweight. Each emitted value uses `{ y: number; roof: boolean }`.
6161

6262
- `tpPerGpu`, `outputTputPerGpu`, `inputTputPerGpu` — raw throughput from the entry (tok/s/gpu).
63+
- `tokenRevenuePerGpuHour` — gross revenue in $/GPU/hr. `buildDerivedChartFields` emits the normalized default (`tputPerGpu × 3,600 ÷ 1,000,000`, with input and output both priced at $1/M). `applyTokenRevenuePricing` can replace it with the selected model's current public OpenRouter prompt/completion prices. Aggregate rows use their measured input/output split; disaggregated rows derive a like-for-like split from fixed ISL:OSL or measured agentic prompt:generation totals because their raw input/output rates use different prefill/decode GPU denominators.
6364
- `tpPerMw` — `(tputPerGpu * 1000) / hardwarePower` (GPU power in kW, result in tok/s/MW).
6465
- Cost-per-million fields — GPU hourly cost divided by tokens-per-hour (in millions): `costh` / `costn` / `costr` for hyperscaler / neocloud / 3-year-rental pricing respectively. Three token variants exist: combined (`costh`/`costn`/`costr`), output-only (`costhOutput`/`costnOutput`/`costrOutput`), and input-only (`costhi`/`costni`/`costri`).
6566
- Tokens-per-dollar fields — tokens-per-hour divided by the same hourly costs. They are separate Y-axis metrics rather than a display toggle: combined (`tokensPerDollarH`/`N`/`R`), output-only (`outputTokensPerDollarH`/`N`/`R`), and input-only (`inputTokensPerDollarH`/`N`/`R`). This keeps existing `i_metric=y_cost*` links and cost semantics unchanged.

‎docs/index.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Design rationale and non-obvious conventions. See [CLAUDE.md](../CLAUDE.md) for
1212
- [TCO Calculator](./tco-calculator.md) — Why interpolation, composite keys, cost matrix, token type bugs, badge logic, state design
1313
- [Adding Entities](./adding-entities.md) — Step-by-step checklists for adding new models, GPUs, precisions, sequences, frameworks (ingest + constants + frontend), plus featuring a day-0 model (launch banner, modal, Quick Comparisons preset)
1414
- [Testing](./testing.md) — Requirements, quality standards, pre-commit checklist
15+
- [Typography](./typography.md) — Type tokens (text-2xs/3xs, named tracking), Heading/Eyebrow components, TS-based chart font sizes, and the check:typography ratchet gate + allowlist burndown
1516
- [Data Transforms](./data-transforms.md) — Full pipeline from BenchmarkRow to RenderableGraph: type hierarchy, hardware key construction, derived metrics, memoization strategy
1617
- [State Ownership](./state-ownership.md) — Which context owns which state, availability filtering cascade, comparison date mechanics, URL param sync
1718
- [Blog](./blog.md) — MDX content system, SEO features (OG images, RSS, llms.txt, JSON-LD), TOC sidebar, reading progress, heading links, analytics events

‎docs/state-ownership.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ them.
113113
Axis and presentation state:
114114

115115
- selected x-axis and y-axis metrics, percentile, and effective x-axis mode
116+
- token-revenue price source (`i_revenue`): normalized $1/M or the selected model's live OpenRouter catalog prices
116117
- scale, optimal-point, label, contrast, legend, and overlay controls
117118

118119
Display changes stay in this domain. A contrast or label toggle therefore does not

‎docs/tco-calculator.md‎

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -167,13 +167,27 @@ at every interpolated point.
167167
compare interpolation models on a fixed snapshot, but they must not be presented
168168
as permanent impact figures for the changing live dataset.
169169

170-
The `/inference` page also exposes tokens-per-dollar as separate Y-axis metrics;
171-
it does not replace the cost-per-million metrics. Historical trends for these
172-
purchasing-power metrics select Pareto knots from the matching total, output, or
173-
input throughput, spline that throughput, and apply the constant
174-
`3600 / $/GPU-hr` multiplier. Reusing the matching throughput frontier is
175-
essential: total-throughput knots are not necessarily the output- or
176-
input-throughput Pareto knots.
170+
The `/inference` page also exposes tokens-per-dollar and normalized token revenue
171+
as separate Y-axis metrics; neither replaces the cost-per-million metrics.
172+
Historical trends for these proportional metrics select Pareto knots from the
173+
matching throughput, spline that throughput, and apply the constant multiplier.
174+
Tokens-per-dollar uses `3600 / $/GPU-hr`; normalized token revenue uses
175+
`3600 / 1,000,000` at the axis's explicit `$1/M tok` sale price. Reusing the
176+
matching throughput frontier is essential: total-throughput knots are not
177+
necessarily the output- or input-throughput Pareto knots.
178+
179+
The revenue axis defaults to pricing every input and output token at the same
180+
`$1/M tok` rate, making it a model-independent SLA comparison. Its OpenRouter
181+
option fetches the selected model's current public prompt/completion prices from
182+
`https://openrouter.ai/api/v1/models` and prices the streams separately. Aggregate
183+
rows use their measured input/output split. Disaggregated rows cannot use the raw
184+
per-prefill/per-decode rates together, so they apply the fixed ISL:OSL shape or
185+
the measured agentic prompt:generation mix to total tok/s/GPU. The OpenRouter
186+
option uses standard prompt/completion prices and does not apply cache discounts.
187+
188+
Neither mode is the fleet lifecycle section's realized-revenue model: it does not
189+
model availability, rollout, or a user-supplied cached-input discount. Those
190+
business assumptions remain in Fleet Lifecycle below.
177191

178192
### The consistency guard
179193

@@ -375,8 +389,9 @@ Four conventions the numbers depend on:
375389
energised, not from the moment they are loaded. So the opening rollout is paid for
376390
in full while it earns its way up from zero, which is what puts the first months
377391
below the rule and gives payback its meaning.
378-
- **The ramp is an assumption; the steps are not.** It defaults to a nominal
379-
quarter (`c_ramp`), and 0 means every config takes effect instantly.
392+
- **The ramp is an assumption; the steps are not.** It defaults to half a month
393+
(`c_ramp`) and moves in quarter-month increments; 0 means every config takes
394+
effect instantly.
380395
- **Markers sit at the release instant** — the foot of each rollout, not its top —
381396
so every dot on the chart is a sweep the user can open.
382397
- **Interrupts are an availability haircut, not drawn events.** A 24-day MTBI
@@ -980,6 +995,6 @@ against that allowlist so a stale or hand-edited link falls back to `margin` rat
980995
than seeding an unknown metric).
981996
The first two default to
982997
`''` in `PARAM_DEFAULTS` because their real defaults are derived, not constant — see
983-
the comment there. The MW budget is
984-
`c_mw`, owned by `ThroughputCalculatorDisplay` and passed to both the fleet
985-
planner and this section so one input drives both.
998+
the comment there. The MW budget is `c_mw`, defaults to 10 MW, and is shared by
999+
the calculator and Fleet Lifecycle page so a budget set on either seeds the
1000+
other.

‎docs/typography.md‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Typography
2+
3+
How text is sized and spaced in the app, and the ratchet gate that keeps it that way.
4+
5+
## Tokens
6+
7+
Defined in the `@theme` block of `packages/app/src/app/globals.css`:
8+
9+
| Token | Class | Value | Use for |
10+
| ------------------------- | ----------------------- | ---------------- | ------------------------------------------------- |
11+
| `--text-2xs` | `text-2xs` | 0.6875rem (11px) | Micro labels one step below `text-xs` |
12+
| `--text-3xs` | `text-3xs` | 0.625rem (10px) | Densest labels: table meta, tooltips, log gutters |
13+
| `--tracking-eyebrow` | `tracking-eyebrow` | 0.16em | Uppercase eyebrow/kicker labels |
14+
| `--tracking-eyebrow-wide` | `tracking-eyebrow-wide` | 0.2em | Extra-wide uppercase labels |
15+
| `--tracking-heading` | `tracking-heading` | -0.04em | Tightened display/hero headings |
16+
17+
Rules of thumb:
18+
19+
- Body and heading sizes use the standard Tailwind scale (`text-xs` … `text-6xl`). Do **not** add wrapper components for body text.
20+
- Never write `text-[11px]`, `text-[0.65rem]`, `tracking-[0.16em]`, etc. — the gate (below) blocks new arbitrary font sizes and letter-spacing values. `tracking-[0.1em]` is exactly `tracking-widest`; use the built-in.
21+
- The micro size tokens deliberately define **no paired line-height**: the arbitrary values they replaced set only `font-size` and inherited line-height, and dense tables depend on that. Don't add `--text-2xs--line-height` without re-checking those surfaces.
22+
- The custom tracking tokens are registered with tailwind-merge in `src/lib/utils.ts` (`extendTailwindMerge`); if you add a token, add it there too or `cn()` will keep conflicting classes.
23+
24+
## Components
25+
26+
- `<Heading>` (`src/components/ui/heading.tsx`) — cva-based visual heading levels (`display`, `page`, `section`, `card`, `label`) with the rendered tag chosen via `as`, so outline and visual size stay independent. New pages should use it instead of retyping size/weight/tracking recipes; existing headings migrate file-by-file as pages get touched — no sweep.
27+
- `<Eyebrow>` (`src/components/ui/eyebrow.tsx`) — the uppercase mono kicker label (`tone="brand" | "muted"`, `wide` for 0.2em tracking).
28+
29+
Blog/article typography is owned by `@tailwindcss/typography` (`prose`) and is out of scope — don't migrate MDX content styles to these tokens.
30+
31+
## Chart text
32+
33+
Chart font sizes live in TypeScript, not CSS variables: `CHART_TYPE` in `src/lib/d3-chart/typography.ts`, with the `px()` helper for `.attr('font-size', …)`. The PNG export path (`useChartExport`) serializes the chart with html-to-image, which cannot resolve `var(--*)`; its `resolveCssVarsForExport()` only bakes color-type attributes, so a CSS-variable font-size silently collapses in exports. The shared export font stacks (`CHART_FONT_SANS`, `CHART_FONT_MINECRAFT`) live in the same module.
34+
35+
OG-image renderers (Satori: `opengraph-image.tsx`, `og-image-render.tsx`, `compare-og.tsx`) draw on a fixed canvas and intentionally hardcode display sizes — they are exempt from all of this.
36+
37+
## The ratchet gate
38+
39+
`bun run check:typography` (wired into CI's lint workflow and lefthook pre-commit) scans `packages/app/src` for:
40+
41+
1. `arbitrary-text-size` — `text-[<number>…]` in any `.ts`/`.tsx` (hex colors like `text-[#ed1c24]` are out of scope);
42+
2. `arbitrary-tracking` — any `tracking-[…]`;
43+
3. `chart-font-literal` — quoted `font-size` literals inside `src/lib/d3-chart/`.
44+
45+
Per-file counts are compared against `packages/app/scripts/typography-allowlist.json`:
46+
47+
- **A count above the allowlist (or a hit in an unlisted file) fails immediately** — fix it with a token, `CHART_TYPE`, or the standard scale.
48+
- **A count below the allowlist also fails**, telling you to run `bun run check:typography --update` — so the allowlist only ever shrinks. It doubles as the migration burndown; there is no deadline, offenders migrate when their file is next touched.
49+
- Matching logic lives in `src/lib/typography-gate.ts` (unit-tested); exemptions (OG renderers, tests, the gate itself) are defined there.

‎lefthook.yml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,6 @@ pre-commit:
1111

1212
- name: typecheck
1313
run: bun run typecheck
14+
15+
- name: typography
16+
run: bun run check:typography

0 commit comments

Comments
 (0)