HTML 77.2%
TypeScript 10.5%
Python 9.6%
JavaScript 2.5%
1import type { Metadata } from 'next';2import Link from 'next/link';3import { RailFilters } from '@/components/changes/rail-filters';4import { TerminalLayout } from '@/components/layout/terminal';5import { ActiveFilters } from '@/components/listing/filters';6import { BreadcrumbLd } from '@/components/meta/breadcrumb-ld';7import { Chip, EntityBadge } from '@/components/ui/badges';8import { DataTable, EmptyRow, Td, Th } from '@/components/ui/data-table';9import { EntityLink } from '@/components/ui/entity';10import { Hint } from '@/components/ui/hint';11import { Pagination, withParams } from '@/components/ui/pagination';12import { Container, Note, PageHeader } from '@/components/ui/section';13import { EmptyState, Unavailable } from '@/components/ui/unavailable';14import { api, safe } from '@/lib/api';15import { fmtDate, fmtInt } from '@/lib/format';16import { routes, SITE_NAME } from '@/lib/site';17import type { EntityDetail, EntitySummary } from '@/lib/types';1819export const metadata: Metadata = { title: 'AI research papers — authors, models introduced, datasets, benchmarks', description: 'Research papers in the atlas with their authors, publication dates, arXiv categories and the models, datasets and benchmarks they are linked to — as stated by model cards and paper metadata.', alternates: { canonical: '/papers' } };20export const revalidate = 300;2122type SP = Record<string, string | undefined>;23const LIMIT = 25;24const KEYS = ['q', 'category', 'org', 'since', 'until', 'sort', 'offset'] as const;25const SORTS = [26 { value: 'published', label: 'Recently published' },27 { value: 'updated', label: 'Recently updated' },28 { value: 'name', label: 'Title' },29];3031function str(v: unknown): string | null {32 return typeof v === 'string' && v.trim() ? v : null;33}34function list(v: unknown): string[] {35 return Array.isArray(v) ? v.filter((x) => x !== null && x !== undefined).map(String) : [];36}37type Linked = { models: EntitySummary[]; datasets: EntitySummary[]; benchmarks: EntitySummary[]; code: EntitySummary[] };38function linked(d: EntityDetail | null): Linked {39 const out: Linked = { models: [], datasets: [], benchmarks: [], code: [] };40 if (!d) return out;41 const seen = new Set<string>();42 for (const g of d.relations ?? [])43 for (const it of g.items) {44 if (seen.has(it.id)) continue;45 seen.add(it.id);46 if (it.entity_type === 'model' || it.entity_type === 'artifact') out.models.push(it);47 else if (it.entity_type === 'dataset') out.datasets.push(it);48 else if (it.entity_type === 'benchmark') out.benchmarks.push(it);49 else if (['repository', 'framework', 'library'].includes(it.entity_type)) out.code.push(it);50 }51 return out;52}5354export default async function PapersPage({ searchParams }: { searchParams: Promise<SP> }) {55 const sp = await searchParams;56 const current: Record<string, string | undefined> = {};57 for (const k of KEYS) if (sp[k]) current[k] = sp[k];58 const offset = Math.max(0, Number(current.offset) || 0);59 const sort = current.sort ?? 'published';60 const [page, orgs] = await Promise.all([safe(api.papers({ ...current, sort, limit: LIMIT, offset })), safe(api.companies({ limit: 40, sort: 'models' }))]);61 // "Introduces" needs each paper's relations: fetch the page's details in parallel (local API, ISR-cached).62 const details = page ? await Promise.all(page.items.map((p) => safe(api.entity(p.slug)))) : [];63 const href = (patch: Record<string, string | number | undefined | null>) => withParams('/papers', current, patch);64 const cats = new Map<string, number>();65 for (const p of page?.items ?? []) for (const c of list(p.attributes?.categories)) cats.set(c, (cats.get(c) ?? 0) + 1);66 const activeCount = Object.keys(current).filter((k) => !['sort', 'offset'].includes(k)).length;6768 const filters = (69 <RailFilters70 action="/papers"71 resetHref={routes.papers()}72 testId="papers"73 fields={[74 { kind: 'text', name: 'q', label: 'Title contains', value: current.q, placeholder: 'e.g. mixture of experts' },75 { kind: 'text', name: 'category', label: 'arXiv category', value: current.category, placeholder: 'cs.CL, cs.LG…', list: [...cats.keys()].sort() },76 { kind: 'select', name: 'org', label: 'Organization', value: current.org, remote: 'companies', options: (orgs?.items ?? []).filter((o) => o.slug === current.org).map((o) => ({ value: o.slug, label: o.name })), note: 'Stated publisher only — arXiv metadata carries no affiliation.' },77 { kind: 'row', fields: [{ kind: 'date', name: 'since', label: 'Since', value: current.since }, { kind: 'date', name: 'until', label: 'Until', value: current.until }] },78 { kind: 'select', name: 'sort', label: 'Sort', value: sort, any: SORTS[0]!.label, options: SORTS.slice(1) },79 ]}80 />81 );82 const inspector = (83 <div className="space-y-4 text-sm">84 <div>85 <p className="eyebrow mb-1.5">Categories on this page</p>86 {cats.size === 0 ? (87 <p className="text-xs text-ink-3">—</p>88 ) : (89 <ul className="flex flex-wrap gap-1">90 {[...cats.entries()]91 .sort((a, b) => b[1] - a[1])92 .slice(0, 16)93 .map(([c, n]) => (94 <li key={c}>95 <Link href={href({ category: c, offset: undefined })} className="inline-flex h-7 items-center gap-1 border border-rule px-1.5 text-xs text-ink-2 hover:border-rule-strong hover:text-ink">96 <span className="mono">{c}</span> <span className="tnum text-ink-3">{n}</span>97 </Link>98 </li>99 ))}100 </ul>101 )}102 </div>103 <p className="text-xs text-ink-3">104 “Introduces” lists the models whose cards cite the paper (<span className="mono">described_by</span>). Author names link to researcher pages when a record exists. <Link href={`/graph?mode=research`} className="link">Research graph →</Link>105 </p>106 </div>107 );108109 return (110 <>111 <Container wide>112 <BreadcrumbLd items={[{ name: SITE_NAME, href: '/' }, { name: 'Research', href: '/papers' }]} />113 <PageHeader eyebrow="Research" title="Papers" lede="Publications linked to models, labs and benchmarks. Authors, venues and abstracts come from arXiv and publisher pages; model links come from model cards citing the paper." aside={page ? <p className="tnum text-sm text-ink-3">{fmtInt(page.total)} papers</p> : undefined} className="pb-3">114 <ActiveFilters current={current} labels={{ q: 'title', category: 'category', org: 'organization', since: 'since', until: 'until' }} makeHref={(p) => href(p)} className="mt-3" />115 </PageHeader>116 </Container>117 <div className="pb-16">118 <TerminalLayout filters={filters} inspector={inspector} filtersTitle="Filters" inspectorTitle="Context" storageKey="aia-papers-inspector" filterCount={activeCount}>119 {!page ? (120 <Unavailable what="Papers" />121 ) : page.items.length === 0 ? (122 <EmptyState title="No papers match">Try another title, category or organization.</EmptyState>123 ) : (124 <>125 <DataTable caption="Papers" compact scroll>126 <thead>127 <tr>128 <Th>Title</Th>129 <Th>Authors</Th>130 <Th>Organization</Th>131 <Th>Published</Th>132 <Th>133 Introduces <Hint text="Models (and artifacts) whose model card or documentation cites this paper — inbound described_by relations." />134 </Th>135 <Th>Datasets</Th>136 <Th>Benchmarks</Th>137 <Th>Code</Th>138 </tr>139 </thead>140 <tbody>141 {page.items.length === 0 && <EmptyRow cols={8}>No rows.</EmptyRow>}142 {page.items.map((e, i) => {143 const a = e.attributes ?? {};144 const authors = list(a.authors);145 const l = linked(details[i] ?? null);146 const code = str(a.code_url);147 const primary = str(a.primary_category);148 return (149 <tr key={e.id}>150 <Td primary>151 <EntityLink e={e} />152 <span className="mt-0.5 flex flex-wrap items-center gap-1">153 {str(a.arxiv_id) && <span className="mono text-[11px] text-ink-3">arXiv:{str(a.arxiv_id)}</span>}154 {primary && (155 <Link href={href({ category: primary, offset: undefined })}>156 <Chip tone="accent" className="mono">{primary}</Chip>157 </Link>158 )}159 </span>160 </Td>161 <Td label="Authors" className="max-w-[18rem] text-sm text-ink-2">162 {authors.length ? (163 <>164 {authors.slice(0, 3).join(', ')}165 {authors.length > 3 && <span className="tnum text-ink-3"> +{authors.length - 3}</span>}166 </>167 ) : (168 <span className="text-ink-3">—</span>169 )}170 </Td>171 <Td label="Organization" className="text-ink-2">172 {e.organization ? (173 <Link href={routes.entity({ entity_type: 'company', slug: e.organization.slug })} className="hover:text-accent">174 {e.organization.name}175 </Link>176 ) : str(a.venue) ? (177 <span className="text-xs">{str(a.venue)}</span>178 ) : (179 <span className="text-ink-3">—</span>180 )}181 </Td>182 <Td label="Published" className="tnum whitespace-nowrap text-ink-2">{str(a.published_at) ? fmtDate(str(a.published_at)) : <span className="text-ink-3">—</span>}</Td>183 <Td label="Introduces">184 {l.models.length ? (185 <span className="flex flex-wrap gap-x-2 gap-y-0.5 text-sm">186 {l.models.slice(0, 3).map((m) => (187 <span key={m.id} className="inline-flex items-center gap-1">188 <EntityBadge type={m.entity_type} small />189 <EntityLink e={m} />190 </span>191 ))}192 {l.models.length > 3 && <span className="tnum text-xs text-ink-3">+{l.models.length - 3}</span>}193 </span>194 ) : details[i] === null ? (195 <span className="text-ink-3">unavailable</span>196 ) : (197 <span className="text-ink-3">—</span>198 )}199 </Td>200 <Td label="Datasets" className="text-sm">{l.datasets.length ? l.datasets.slice(0, 2).map((x) => <EntityLink key={x.id} e={x} className="mr-2" />) : <span className="text-ink-3">—</span>}</Td>201 <Td label="Benchmarks" className="text-sm">{l.benchmarks.length ? l.benchmarks.slice(0, 2).map((x) => <EntityLink key={x.id} e={x} className="mr-2" />) : <span className="text-ink-3">—</span>}</Td>202 <Td label="Code" className="text-sm">203 {l.code.length ? (204 l.code.slice(0, 2).map((x) => <EntityLink key={x.id} e={x} className="mr-2" />)205 ) : code ? (206 <a href={code} target="_blank" rel="noopener noreferrer" className="link text-xs">207 {code.replace(/^https?:\/\/(www\.)?/, '').slice(0, 32)}208 </a>209 ) : (210 <span className="text-ink-3">—</span>211 )}212 </Td>213 </tr>214 );215 })}216 </tbody>217 </DataTable>218 <Pagination total={page.total} limit={LIMIT} offset={offset} makeHref={(o) => href({ offset: o || undefined })} className="mt-4" />219 <Note className="mt-3">Author lists and categories are copied from the paper's own metadata (arXiv, publisher). Linked models, datasets, benchmarks and code come from stated relations only; a dash means no source stated one.</Note>220 </>221 )}222 </TerminalLayout>223 </div>224 </>225 );226}227