import type { Metadata } from 'next';
import { ScrollX } from '@/components/models/scroll-x';
import Link from 'next/link';
import { CompareTrayBar } from '@/components/compare/compare-tray-bar';
import { FilterBar } from '@/components/listing/filters';
import { Chip, EntityBadge } from '@/components/ui/badges';
import { DataTable, EmptyRow, Td, Th } from '@/components/ui/data-table';
import { Hint } from '@/components/ui/hint';
import { Pagination, withParams } from '@/components/ui/pagination';
import { Container, Note, PageHeader } from '@/components/ui/section';
import { Unavailable } from '@/components/ui/unavailable';
import { apiD1, safe } from '@/lib/api';
import { fmtDate, fmtInt, fmtParams, num } from '@/lib/format';
import { routes, SITE_NAME, SITE_URL } from '@/lib/site';
export const metadata: Metadata = {
title: 'Model families — releases, parameter ranges, licences & benchmark ranks',
description: 'Model families (Llama, Qwen, Claude, Gemma…) grouping canonical releases: member count, first and latest release, parameter range, modalities, licence mix and best benchmark ranks — every number from sourced claims.',
alternates: { canonical: '/families' },
openGraph: { title: `Model families | ${SITE_NAME}`, url: `${SITE_URL}/families`, type: 'website' },
};
export const revalidate = 600;
type SP = Record;
const LIMIT = 50;
const SORTS = [
{ value: 'models', label: 'Most models' },
{ value: 'last_release', label: 'Latest release' },
{ value: 'name', label: 'Name' },
];
export default async function FamiliesPage({ searchParams }: { searchParams: Promise }) {
const sp = await searchParams;
const current: SP = {};
for (const k of ['q', 'org', 'sort', 'offset']) if (sp[k]) current[k] = sp[k];
const offset = Math.max(0, Number(current.offset) || 0);
const sort = current.sort ?? 'models';
const [page, bench] = await Promise.all([safe(apiD1.families({ q: current.q, org: current.org, sort, limit: LIMIT, offset })), safe(apiD1.benchmarks())]);
const benchName = new Map((bench?.items ?? []).map((b) => [b.slug, b.name]));
const href = (patch: Record) => withParams('/families', current, patch);
const nonCanonical = (page?.items ?? []).filter((f) => !f.canonical).length;
const ld = { '@context': 'https://schema.org', '@type': 'CollectionPage', name: 'Model families', url: `${SITE_URL}/families`, description: metadata.description, ...(page ? { mainEntity: { '@type': 'ItemList', numberOfItems: page.total, itemListElement: page.items.slice(0, 10).map((f, i) => ({ '@type': 'ListItem', position: offset + i + 1, name: f.name, url: `${SITE_URL}${routes.family(f.slug)}` })) } } : {}) };
return (
{fmtInt(page.total)} families
: undefined}>
{!page ? (
) : (
<>
{nonCanonical > 0 && (
{fmtInt(nonCanonical)} row{nonCanonical === 1 ? '' : 's'} on this page are label-based families (not yet canonical entities)
)}
| Family |
Organization |
Models |
First · latest release |
Parameters |
Modalities |
Licences |
Best benchmark ranks |
{page.items.length === 0 && No family matches.}
{page.items.map((f) => {
const ranks = Object.entries(f.benchmark_best ?? {})
.sort((a, b) => a[1].rank - b[1].rank)
.slice(0, 3);
const pmin = num(f.param_range?.min);
const pmax = num(f.param_range?.max);
return (
|
{f.name}
{!f.canonical && (
label
)}
|
{f.organization ? (
{f.organization.name}
) : (
'—'
)}
|
{fmtInt(f.model_count)}
|
{f.first_release ? fmtDate(f.first_release) : '—'} · {f.last_release ? fmtDate(f.last_release) : '—'}
|
{pmin === null ? '—' : pmin === pmax ? fmtParams(pmin) : `${fmtParams(pmin)} – ${fmtParams(pmax)}`}
|
{f.modalities?.length ? f.modalities.map((m) => {m}) : —}
|
{f.licenses?.length ? (
{f.licenses.slice(0, 3).map((l) => (
{l.key} {fmtInt(l.models)}
))}
{f.licenses.length > 3 && +{f.licenses.length - 3}}
) : (
—
)}
|
{ranks.length ? (
{ranks.map(([slug, r]) => (
{(benchName.get(slug) ?? slug).replace('Artificial Analysis ', 'AA ')} #{fmtInt(r.rank)}
))}
) : (
no results
)}
|
);
})}
href({ offset: o || undefined })} className="mt-4" />
{page.note ?? 'Families are canonical model_family entities; legacy labels appear as label-based rows until canonicalised.'}
>
)}
);
}