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 { Tri } from '@/components/models/badges';5import { LICENCE_DIMENSIONS } from '@/components/models/openness-block';6import { DataTable, EmptyRow, Td, Th } from '@/components/ui/data-table';7import { Hint } from '@/components/ui/hint';8import { Container, Note, PageHeader } from '@/components/ui/section';9import { Unavailable } from '@/components/ui/unavailable';10import { apiD1, safe } from '@/lib/api';11import { cn } from '@/lib/cn';12import { fmtInt, num } from '@/lib/format';13import { SITE_NAME, SITE_URL } from '@/lib/site';1415export const metadata: Metadata = {16 title: 'Model licences — commercial use, redistribution, derivatives, hosting restrictions',17 description: 'Canonical licences of AI models (Apache-2.0, MIT, Llama Community, Gemma Terms, RAIL…): category, commercial use, redistribution, derivatives, hosting restrictions, acceptable-use policy and the number of canonical models under each — permissions read from the licence text, unknown when ambiguous.',18 alternates: { canonical: '/licenses' },19 openGraph: { title: `Model licences | ${SITE_NAME}`, url: `${SITE_URL}/licenses`, type: 'website' },20};21export const revalidate = 3600;2223const CATEGORY_HINT: Record<string, string> = {24 permissive: 'OSI-style licences with no field-of-use restriction (Apache, MIT, BSD).',25 copyleft: 'Derivatives must be shared under the same terms (GPL, AGPL, LGPL, MPL).',26 'creative-commons': 'Creative Commons terms; NC variants forbid commercial use, ND forbids derivatives.',27 'responsible-ai': 'RAIL / OpenRAIL licences: permissive with an attached acceptable-use policy.',28 community: 'Vendor community licences (Llama, Gemma, Qwen, NVIDIA…): downloadable weights with user caps, hosting or field-of-use clauses.',29 'research-only': 'Non-commercial / research-only terms.',30 proprietary: 'No weights redistribution; access through an API or product.',31 unknown: 'Stated licence text could not be classified.',32};3334export default async function LicensesPage({ searchParams }: { searchParams: Promise<{ category?: string }> }) {35 const { category } = await searchParams;36 const res = await safe(apiD1.licenses());37 const all = (res?.items ?? []).slice().sort((a, b) => (num(b.models) ?? 0) - (num(a.models) ?? 0) || a.label.localeCompare(b.label));38 const items = category ? all.filter((l) => l.category === category) : all;39 const cats = (res?.categories ?? []).map((c) => ({ c, n: all.filter((l) => l.category === c).length, models: all.filter((l) => l.category === c).reduce((s, l) => s + (num(l.models) ?? 0), 0) })).filter((x) => x.n > 0);40 const chip = (active: boolean) => cn('inline-flex h-8 items-center gap-1.5 border px-2.5 text-xs whitespace-nowrap', active ? 'border-ink bg-ink text-canvas' : 'border-rule text-ink-2 hover:border-rule-strong hover:text-ink');41 const ld = { '@context': 'https://schema.org', '@type': 'CollectionPage', name: 'Model licences', url: `${SITE_URL}/licenses`, description: metadata.description, mainEntity: { '@type': 'ItemList', numberOfItems: all.length, itemListElement: all.slice(0, 12).map((l, i) => ({ '@type': 'ListItem', position: i + 1, name: l.label, url: `${SITE_URL}/licenses/${encodeURIComponent(l.key)}` })) } };4243 return (44 <Container wide>45 <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(ld) }} />46 <PageHeader eyebrow="Licences" title="Model licences" lede="The canonical licence ontology behind the openness classification. Permissions are read from each licence text; a dash means the text is ambiguous — never assumed." aside={res ? <p className="tnum text-sm text-ink-3">{fmtInt(items.length)}{category ? ` of ${fmtInt(all.length)}` : ''} licences</p> : undefined}>47 {cats.length > 0 && (48 <nav aria-label="Category" className="no-scrollbar -mx-4 mt-6 flex gap-1.5 overflow-x-auto px-4 md:mx-0 md:flex-wrap md:px-0">49 <Link href="/licenses" className={chip(!category)} aria-current={!category ? 'true' : undefined}>50 All <span className="tnum opacity-70">{fmtInt(all.length)}</span>51 </Link>52 {cats.map(({ c, n, models }) => (53 <Link key={c} href={`/licenses?category=${encodeURIComponent(c)}`} className={chip(category === c)} aria-current={category === c ? 'true' : undefined} title={`${CATEGORY_HINT[c] ?? c} · ${fmtInt(models)} models`}>54 {c} <span className="tnum opacity-70">{fmtInt(n)}</span>55 </Link>56 ))}57 </nav>58 )}59 </PageHeader>60 <div className="pb-16">61 {!res ? (62 <Unavailable what="Licences" />63 ) : (64 <>65 {category && CATEGORY_HINT[category] && <p className="mb-3 text-sm text-ink-2">{CATEGORY_HINT[category]}</p>}66 <ScrollX><DataTable caption="Licences" compact>67 <thead>68 <tr>69 <Th>Licence</Th>70 <Th>Category</Th>71 {LICENCE_DIMENSIONS.slice(0, 5).map((d) => (72 <Th key={d.key} title={d.hint} className="cursor-help underline decoration-dotted decoration-rule-strong underline-offset-2">73 {d.label}74 </Th>75 ))}76 <Th num>Models</Th>77 </tr>78 </thead>79 <tbody>80 {items.length === 0 && <EmptyRow cols={8}>No licence in this category.</EmptyRow>}81 {items.map((l) => (82 <tr key={l.key}>83 <Td primary>84 <Link href={`/licenses/${encodeURIComponent(l.key)}`} className="text-ink hover:text-accent hover:underline">85 {l.label}86 </Link>87 <span className="mono block text-[11px] text-ink-3">88 {l.key}89 {l.spdx && l.spdx !== l.key ? ` · SPDX ${l.spdx}` : ''}90 {l.osi_approved ? ' · OSI' : ''}91 </span>92 </Td>93 <Td label="Category" className="text-ink-2">94 <Link href={`/licenses?category=${encodeURIComponent(l.category)}`} className="hover:text-accent">95 {l.category}96 </Link>97 </Td>98 {LICENCE_DIMENSIONS.slice(0, 5).map((d) => (99 <Td key={d.key} label={d.label}>100 <Tri v={l[d.key] as boolean | null} yes={d.invert ? 'Yes — restricted' : 'Yes'} no={d.invert ? 'None' : 'No'} />101 </Td>102 ))}103 <Td num label="Models" className="tnum">104 {num(l.models) ? (105 <Link href={`/models?license=${encodeURIComponent(l.key)}`} className="hover:text-accent">106 {fmtInt(l.models)}107 </Link>108 ) : (109 <span className="text-ink-3">0</span>110 )}111 </Td>112 </tr>113 ))}114 </tbody>115 </DataTable></ScrollX>116 {res.unclassified?.length > 0 && (117 <section className="mt-8">118 <p className="eyebrow mb-2">119 Unclassified licence labels <span className="tnum text-ink-3">{fmtInt(res.unclassified.length)}</span>120 </p>121 <ul className="flex flex-wrap gap-1.5 text-xs">122 {res.unclassified.map((u) => (123 <li key={u.raw} className="border border-dashed border-rule-strong px-2 py-1 text-ink-2">124 “{u.raw}” <span className="tnum text-ink-3">{fmtInt(u.models)}</span>125 </li>126 ))}127 </ul>128 </section>129 )}130 <Note className="mt-3">{res.note ?? 'Permissions are read from the licence text (null = the text is ambiguous). Counts cover canonical models only.'} Hosting restrictions / acceptable-use: “Yes — restricted” means the clause exists.</Note>131 <dl className="mt-3 grid gap-x-6 gap-y-1 text-[11px] text-ink-3 sm:grid-cols-2 lg:grid-cols-3" data-licence-definitions>132 {LICENCE_DIMENSIONS.map((d) => (133 <div key={d.key} className="flex gap-1.5">134 <dt className="shrink-0 font-medium text-ink-2">{d.label}</dt>135 <dd className="flex items-center gap-1">136 {d.hint}137 <Hint text={d.hint} align="right" className="hidden" />138 </dd>139 </div>140 ))}141 </dl>142 </>143 )}144 </div>145 </Container>146 );147}148