Skip to content

feat: gpu compare#351

Merged
adibarra merged 28 commits into
masterfrom
feat/gpu-compare-v2
May 15, 2026
Merged

feat: gpu compare#351
adibarra merged 28 commits into
masterfrom
feat/gpu-compare-v2

Conversation

@adibarra
Copy link
Copy Markdown
Contributor

@adibarra adibarra commented May 15, 2026

Fixes #267
Supersedes #332

github-actions Bot and others added 25 commits May 8, 2026 20:12
Issue #267. New top-level route that renders any pair of GPUs from the
HW_REGISTRY as a focused, indexable head-to-head comparison.

- `/compare/[slug]` is server-rendered (`force-dynamic`) and reads from the
  same `cachedQuery` blob cache used by `/api/v1/benchmarks`, so the
  existing `/api/v1/invalidate` endpoint (which calls `purgeAll()` →
  `blobPurge()` + `revalidateTag('db')`) also flushes the compare pages
  on each ingest. No time-based revalidation.
- Slug = `<a>-vs-<b>`. Canonical = alphabetical; non-canonical slugs
  redirect (307) to canonical so we get one URL per pair.
- JSON-LD `ItemList` of `Product` entries with vendor, architecture, TDP,
  and best-in-class throughput / TTFT / TPOT pulled from the actual
  benchmark data — consumable by search engines and LLMs.
- Reuses `<InferenceProvider>` + `<InferenceChartDisplay>` so the chart,
  table, controls, PNG/CSV export, pan/zoom, and unofficial-run overlay
  path all carry over unchanged.
- Server picks `(precision, sequence)` defaults that maximise overlap
  between the two GPUs in the pair (so `/compare/h100-vs-h200` lands on
  FP8 instead of the global FP4 default which has no Hopper data). New
  `initialModel` / `initialSequence` / `initialPrecisions` props on
  `GlobalFilterProvider` take effect only when the URL has no override.
- `InferenceProvider` gains `initialActiveHwTypes` to seed the legend
  filter from the slug. The existing `pendingActiveHwTypes` consumer now
  understands bare GPU prefixes (e.g. `h100`) in addition to full
  framework-suffixed hwKeys, so we can pass `[a, b]` without knowing
  which framework configs exist.
- OG image generator at `/compare/[slug]/opengraph-image` produces a
  vendor-coloured side-by-side `A vs B` PNG.
- Sitemap includes all C(n, 2) canonical pairs.
- 14 unit tests for slug parse / canonicalize / pair-enumeration.

Verified in Playwright:
- /compare/h100-vs-h200 → FP8 auto-selected, H100/H200 active in legend,
  others dimmed; both charts render real data.
- /compare/b200-vs-mi355x (cross-vendor) → both shown, real data.
- /compare/h200-vs-h100 → 307 → /compare/h100-vs-h200.
- /compare/a100-vs-h100 → 404.
- OG image renders correctly.

Co-authored-by: functionstackx <functionstackx@users.noreply.github.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 15, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
inferencemax-app Ready Ready Preview, Comment May 15, 2026 4:04pm

Request Review

@adibarra adibarra marked this pull request as ready for review May 15, 2026 15:41
Comment thread packages/app/src/components/json-ld.test.ts Fixed
Comment thread packages/app/src/components/json-ld.test.ts Fixed
Comment thread packages/app/src/components/json-ld.test.ts Fixed
@adibarra adibarra merged commit f347e1b into master May 15, 2026
15 checks passed
@adibarra adibarra deleted the feat/gpu-compare-v2 branch May 15, 2026 16:10
functionstackx added a commit that referenced this pull request May 26, 2026
…ible redirects (#382)

* feat(compare): extend SSR /compare to all models with model-prefixed URLs

PR #351 introduced /compare/[a]-vs-[b] but hardcoded DeepSeek R1 as the
only model the SSR fetch, OG image, JSON-LD, and metadata understand.
This adds a model prefix to the slug so every model the dashboard knows
about gets its own SSR'd, indexable comparison page for every GPU pair.

URL format
==========
- New canonical: /compare/{model-slug}-{a}-vs-{b}
- Bare-slug back-compat: /compare/{a}-vs-{b}  ->  one-hop 307 to
  /compare/deepseek-r1-{a}-vs-{b} so PR #351 inbound links still resolve.
- Query params survive the redirect (?i_seq=1k/1k&i_prec=fp8 etc.) — the
  prior redirect logic dropped them, which would have broken bare-slug
  shared links.

Model slug registry (packages/app/src/lib/compare-slug.ts):
  deepseek-r1, deepseek-v4, kimi-k26, qwen-3-5, glm-5-1, minimax-m27,
  llama-3-3-70b, gptoss-120b  (8 canonical slugs).

Alias slugs that 307 to the canonical version (family-level shortcuts +
older-version supersession):
  deepseek -> deepseek-r1
  kimi, kimi-k25 -> kimi-k26
  glm, glm-5 -> glm-5-1   (same architecture, newer point release wins)
  minimax, minimax-m25 -> minimax-m27
  qwen -> qwen-3-5
  llama -> llama-3-3-70b
  gptoss -> gptoss-120b

URL slugs are deliberately finer-grained than the dashboard's
display-grouped model dropdown — kimi-k25 and kimi-k26 are distinct
slugs even though both DB keys roll up to one "Kimi-K2.5" display
option in the UI. Each slug queries a single dbKey so the data shown
matches the URL exactly.

Parser
======
parseCompareSlug now returns { model, a, b, isLegacyBareSlug,
isAliasModel }. GPU keys contain no hyphens (h100, gb200, mi355x), so
the parser finds the {model}/{a} split by lastIndexOf('-') in the
left half — unambiguous even when the model slug contains hyphens like
deepseek-r1 or glm-5-1. The page-level handler issues one 307 to the
fully canonical URL for any combination of (bare slug + alias model +
non-canonical GPU order).

Per-model data fetch
====================
getCachedBenchmarks(parsed.model.dbKeys) replaces the hardcoded
['dsr1']. The query cache is already keyed on the dbKeys array, so
each model gets its own cache slot with zero extra cache work.

generateStaticParams + sitemap
==============================
allCanonicalCompareSlugs() emits the (canonical models × pairs) cross
product = 8 * 36 = 288 entries. Sitemap jumps from 36 to 288 URLs.
Index page (compare/page.tsx) becomes one section per model, each with
the existing NVIDIA-vs-NVIDIA / AMD-vs-AMD / cross-vendor sub-grid.

OG image + metadata + JSON-LD + page header
===========================================
- OG eyebrow now reads "{model.label} · Head-to-head GPU benchmark"
- Page metadata title is "{model.label} — {GPU label} Inference Benchmark"
- JSON-LD ItemList.name and Dataset.name include the model name, and
  each comparison Observation includes a Model PropertyValue so search
  engines understand "this is a per-model comparison"
- Page-client header eyebrow shows "{model.label} · GPU comparison"
  above the H1 so the URL-grouping is legible without scanning the URL.

Tests
=====
- compare-slug.test.ts: 32 unit tests covering new canonical form,
  alias resolution (every alias key), bare-slug detection, every
  canonical model round-trip through parser + canonicalizer, rejection
  cases (unknown model, unknown GPU, same GPU twice, malformed).
- compare-redirect.cy.ts: 9 e2e cases covering bare-slug redirect,
  alias redirect (kimi -> kimi-k26), older-version redirect
  (kimi-k25 -> kimi-k26, glm-5 -> glm-5-1), reversed GPU order through
  any redirect class, query-param preservation, and non-deepseek
  canonical slugs serving without redirect.
- compare-table.cy.ts + url-params.cy.ts: visit canonical
  model-prefixed URLs directly so assertions are about the rendered
  page, not interleaved with bare-slug redirects.

Backward compatibility
======================
Every URL form that worked under PR #351 — /compare/h100-vs-h200,
/compare/h200-vs-h100 (non-canonical), uppercase — keeps working via
the one-hop 307 redirect chain. ?g_model= URL param still overrides
the slug-derived model on the SSR side for advanced sharers.

Manual verification (dev server):
- 307 Location headers correct for all 5 redirect classes
- Query params preserved: ?i_seq=1k/1k&i_prec=fp8 -> ?i_seq=1k%2F1k&i_prec=fp8
- Invalid model slug -> 404
- Sitemap count: exactly 288 compare URLs
- /compare index: 200 OK

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(compare): filter index, sitemap, and SSG params by real benchmark coverage

Only emit /compare/<model>-<a>-vs-<b> URLs where both GPUs actually have
benchmark rows for that model in Neon. Avoids 156 empty pair cards in the
index and ~123 unreachable sitemap entries (288 -> 165).

- compare-availability.ts: cached server-side helper using getAvailabilityData
- compare/page.tsx: async, filtered modelsWithPairs, per-model card sections
- sitemap.ts: async, only canonical (model, pair) combos with data
- [slug]/page.tsx + opengraph-image.tsx: generateStaticParams now async + filtered
- compare-slug.ts: kimi-k26 / glm-5-1 / minimax-m27 dbKeys now include both
  point releases so the canonical slug pulls existing data while staying
  forward-compatible with the newer DB key when it arrives

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(compare): show both point-release versions in shared-arch slug labels

When a canonical compare slug groups two model point releases sharing one
architecture (kimi-k26 -> [k2.5, k2.6], glm-5-1 -> [5, 5.1], minimax-m27 ->
[m2.5, m2.7]), the header, OG image, metadata title, and index card section
now read "Kimi K2.5/K2.6" / "GLM 5/5.1" / "MiniMax M2.5/M2.7" instead of
just the newer-version name. Makes it obvious the page covers both versions'
benchmark data, not only the newer one.

The minimax-m25 -> minimax-m27 alias redirect already worked via the
existing COMPARE_MODEL_ALIASES map; verified live.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(compare): switch slug redirects from 307 to 308 (permanent)

Bare-slug -> deepseek-r1, alias model -> canonical, older version -> newer
version, and non-canonical GPU order -> alphabetical are all permanent
decisions for this URL space. Using next/navigation's permanentRedirect()
returns 308 instead of 307, letting search engines consolidate link equity
onto the canonical URL instead of keeping the alias indexed separately.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add SSR'd /compare/[a]-vs-[b] head-to-head GPU comparison pages

3 participants