HTML 77.2%
TypeScript 10.5%
Python 9.6%
JavaScript 2.5%
1import type { Metadata } from 'next';2import { ScrollX } from '@/components/models/scroll-x';3import Link from 'next/link';4import { CompareTrayBar } from '@/components/compare/compare-tray-bar';5import { FilterBar } from '@/components/listing/filters';6import { Chip, EntityBadge } from '@/components/ui/badges';7import { DataTable, EmptyRow, Td, Th } from '@/components/ui/data-table';8import { Hint } from '@/components/ui/hint';9import { Pagination, withParams } from '@/components/ui/pagination';10import { Container, Note, PageHeader } from '@/components/ui/section';11import { Unavailable } from '@/components/ui/unavailable';12import { apiD1, safe } from '@/lib/api';13import { fmtDate, fmtInt, fmtParams, num } from '@/lib/format';14import { routes, SITE_NAME, SITE_URL } from '@/lib/site';1516export const metadata: Metadata = {17 title: 'Model families — releases, parameter ranges, licences & benchmark ranks',18 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.',19 alternates: { canonical: '/families' },20 openGraph: { title: `Model families | ${SITE_NAME}`, url: `${SITE_URL}/families`, type: 'website' },21};22export const revalidate = 600;2324type SP = Record<string, string | undefined>;25const LIMIT = 50;26const SORTS = [27 { value: 'models', label: 'Most models' },28 { value: 'last_release', label: 'Latest release' },29 { value: 'name', label: 'Name' },30];3132export default async function FamiliesPage({ searchParams }: { searchParams: Promise<SP> }) {33 const sp = await searchParams;34 const current: SP = {};35 for (const k of ['q', 'org', 'sort', 'offset']) if (sp[k]) current[k] = sp[k];36 const offset = Math.max(0, Number(current.offset) || 0);37 const sort = current.sort ?? 'models';38 const [page, bench] = await Promise.all([safe(apiD1.families({ q: current.q, org: current.org, sort, limit: LIMIT, offset })), safe(apiD1.benchmarks())]);39 const benchName = new Map((bench?.items ?? []).map((b) => [b.slug, b.name]));40 const href = (patch: Record<string, string | number | undefined | null>) => withParams('/families', current, patch);41 const nonCanonical = (page?.items ?? []).filter((f) => !f.canonical).length;42 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)}` })) } } : {}) };4344 return (45 <Container wide>46 <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(ld) }} />47 <PageHeader eyebrow="Families" title="Model families" lede="Families group canonical releases (Llama 3.1, Qwen3, Claude…). Counts, releases and parameter ranges are aggregated from the members' sourced claims; benchmark ranks are the best rank of any member in the benchmark's primary group." aside={page ? <p className="tnum text-sm text-ink-3">{fmtInt(page.total)} families</p> : undefined}>48 <FilterBar action="/families" className="mt-6" resetHref="/families" fields={[{ kind: 'text', name: 'q', label: 'Name', value: current.q, placeholder: 'llama, qwen…' }, { kind: 'text', name: 'org', label: 'Organization slug', value: current.org, placeholder: 'meta-ai' }]} sort={{ value: sort, options: SORTS }} />49 </PageHeader>50 <div className="pb-16">51 {!page ? (52 <Unavailable what="Families" />53 ) : (54 <>55 {nonCanonical > 0 && (56 <p className="mb-2 flex items-center gap-1 text-xs text-ink-3">57 {fmtInt(nonCanonical)} row{nonCanonical === 1 ? '' : 's'} on this page are label-based families (not yet canonical entities)58 <Hint text={page.note ?? 'canonical: true = model_family entity; false = legacy attributes.family label awaiting canonicalisation.'} />59 </p>60 )}61 <ScrollX><DataTable caption="Model families" compact>62 <thead>63 <tr>64 <Th>Family</Th>65 <Th>Organization</Th>66 <Th num>Models</Th>67 <Th>First · latest release</Th>68 <Th num>Parameters</Th>69 <Th>Modalities</Th>70 <Th>Licences</Th>71 <Th>Best benchmark ranks</Th>72 </tr>73 </thead>74 <tbody>75 {page.items.length === 0 && <EmptyRow cols={8}>No family matches.</EmptyRow>}76 {page.items.map((f) => {77 const ranks = Object.entries(f.benchmark_best ?? {})78 .sort((a, b) => a[1].rank - b[1].rank)79 .slice(0, 3);80 const pmin = num(f.param_range?.min);81 const pmax = num(f.param_range?.max);82 return (83 <tr key={f.slug}>84 <Td primary>85 <span className="flex flex-wrap items-center gap-x-2">86 <Link href={routes.family(f.slug)} className="text-ink hover:text-accent hover:underline">87 {f.name}88 </Link>89 {!f.canonical && (90 <span className="inline-flex items-center gap-0.5 text-[10px] uppercase tracking-wide text-warning" title="Label-based family: grouped by the source's family label, not yet a canonical entity">91 label92 </span>93 )}94 </span>95 </Td>96 <Td label="Organization" className="text-ink-2">97 {f.organization ? (98 <Link href={routes.entity({ entity_type: 'company', slug: f.organization.slug })} className="hover:text-accent">99 {f.organization.name}100 </Link>101 ) : (102 '—'103 )}104 </Td>105 <Td num label="Models" className="tnum">106 {fmtInt(f.model_count)}107 </Td>108 <Td label="First · latest release" className="tnum text-ink-2 whitespace-nowrap">109 {f.first_release ? fmtDate(f.first_release) : '—'} <span className="text-ink-3">·</span> {f.last_release ? fmtDate(f.last_release) : '—'}110 </Td>111 <Td num label="Parameters" className="tnum text-ink-2 whitespace-nowrap">112 {pmin === null ? '—' : pmin === pmax ? fmtParams(pmin) : `${fmtParams(pmin)} – ${fmtParams(pmax)}`}113 </Td>114 <Td label="Modalities">115 <span className="flex flex-wrap gap-1">{f.modalities?.length ? f.modalities.map((m) => <Chip key={m}>{m}</Chip>) : <span className="text-ink-3">—</span>}</span>116 </Td>117 <Td label="Licences" className="text-xs text-ink-2">118 {f.licenses?.length ? (119 <span className="flex flex-wrap gap-x-2 gap-y-0.5">120 {f.licenses.slice(0, 3).map((l) => (121 <Link key={l.key} href={`/licenses/${encodeURIComponent(l.key)}`} className="hover:text-accent" title={l.label}>122 {l.key} <span className="tnum text-ink-3">{fmtInt(l.models)}</span>123 </Link>124 ))}125 {f.licenses.length > 3 && <span className="text-ink-3">+{f.licenses.length - 3}</span>}126 </span>127 ) : (128 <span className="text-ink-3">—</span>129 )}130 </Td>131 <Td label="Best benchmark ranks" className="text-xs text-ink-2">132 {ranks.length ? (133 <span className="flex flex-wrap gap-x-2 gap-y-0.5">134 {ranks.map(([slug, r]) => (135 <Link key={slug} href={routes.benchmark(slug)} className="whitespace-nowrap hover:text-accent" title={`${benchName.get(slug) ?? slug}: rank ${r.rank} (${r.model})`}>136 {(benchName.get(slug) ?? slug).replace('Artificial Analysis ', 'AA ')} <span className="tnum font-medium text-ink">#{fmtInt(r.rank)}</span>137 </Link>138 ))}139 </span>140 ) : (141 <span className="text-ink-3">no results</span>142 )}143 </Td>144 </tr>145 );146 })}147 </tbody>148 </DataTable></ScrollX>149 <Pagination total={page.total} limit={LIMIT} offset={offset} makeHref={(o) => href({ offset: o || undefined })} className="mt-4" />150 <Note className="mt-3">151 <EntityBadge type="model_family" small /> {page.note ?? 'Families are canonical model_family entities; legacy labels appear as label-based rows until canonicalised.'}152 </Note>153 </>154 )}155 </div>156 <CompareTrayBar />157 </Container>158 );159}160