HTML 77.2%
TypeScript 10.5%
Python 9.6%
JavaScript 2.5%
1import { ExternalLink, GitFork } from 'lucide-react';2import Link from 'next/link';3import { ChangeRow } from '@/components/changes/change-row';4import { CompareButton } from '@/components/compare/compare-button';5import { Identity, SourcesTable } from '@/components/entity/blocks';6import { DataStrip, SectionNav } from '@/components/layout/terminal';7import { ViewBeacon } from '@/components/layout/view-beacon';8import { BreadcrumbLd, Breadcrumbs } from '@/components/meta/breadcrumb-ld';9import { WatchButton } from '@/components/watchlist/watch-button';10import { Chip, EntityBadge, OpennessBadge, StatusBadge } from '@/components/ui/badges';11import { DataTable, EmptyRow, Td, Th } from '@/components/ui/data-table';12import { EntityLink, QualityMark } from '@/components/ui/entity';13import { Hint } from '@/components/ui/hint';14import { KeyValue } from '@/components/ui/key-value';15import { Container, Note, Section } from '@/components/ui/section';16import { EmptyState } from '@/components/ui/unavailable';17import { api, apiD1, apiD3, safe } from '@/lib/api';18import { fmtAgo, fmtDate, fmtInt, fmtParams, fmtTokens, num } from '@/lib/format';19import { PROSE_KEYS, routes, SITE_NAME, SITE_URL, typeLabel } from '@/lib/site';20import type { ChangeEvent, EntityDetail, EntitySummary, FamilyRow } from '@/lib/types';2122/*23 Organization page 3.0 (server, async): footprint strip → models grouped by family → families → papers → researchers →24 providers operated → repositories/frameworks → timeline split CORPORATE NEWS vs MODEL EVENTS → sources.25 Counts come from the listing APIs (`/models?org=`, `/families?org=`, `/papers?org=`, `/explore/researcher?org=`) and the26 detail relations; what the API does not expose per organization (datasets, benchmarks) is shown as "—" with a definition.27*/2829const ORG_TYPES = ['company', 'organization', 'lab', 'university'];30const CORPORATE = new Set(['ANNOUNCEMENT', 'NEW_COMPANY', 'NEW_ORGANIZATION', 'NEW_LAB', 'NEW_UNIVERSITY', 'PROPERTY_CHANGED']);3132function relItems(d: EntityDetail, types: string[], predicates?: string[]): EntitySummary[] {33 const out: EntitySummary[] = [];34 const seen = new Set<string>();35 for (const g of d.relations ?? []) {36 if (predicates && !predicates.includes(g.predicate)) continue;37 for (const it of g.items) {38 if (!types.includes(it.entity_type) || seen.has(it.id)) continue;39 seen.add(it.id);40 out.push(it);41 }42 }43 return out;44}4546type ModelRow = EntitySummary & { family?: { id: string; slug: string; name: string } | null };4748function ModelsByFamily({ items, total, org }: { items: ModelRow[]; total: number; org: string }) {49 if (!items.length) return <EmptyState title="No canonical model attributed to this organization">Models are attributed through a stated developer relation or an official model id.</EmptyState>;50 const groups = new Map<string, { label: string; href: string | null; items: ModelRow[] }>();51 for (const m of items) {52 const fam = m.family ?? null;53 const label = fam?.name ?? (typeof m.attributes?.family === 'string' ? (m.attributes.family as string) : 'No family');54 const key = fam?.slug ?? `label:${label}`;55 (groups.get(key) ?? groups.set(key, { label, href: fam ? routes.family(fam.slug) : null, items: [] }).get(key)!).items.push(m);56 }57 const ordered = [...groups.values()].sort((a, b) => b.items.length - a.items.length || a.label.localeCompare(b.label));58 return (59 <>60 <DataTable caption="Models by family" compact>61 <thead>62 <tr>63 <Th>Model</Th>64 <Th num>Params</Th>65 <Th num>Context</Th>66 <Th>Openness</Th>67 <Th>Status</Th>68 <Th>Released</Th>69 <Th num>Quality</Th>70 </tr>71 </thead>72 <tbody>73 {ordered.map((g) => (74 <FamilyRows key={g.label} g={g} />75 ))}76 </tbody>77 </DataTable>78 {total > items.length && (79 <Note className="mt-2">80 Showing {fmtInt(items.length)} of {fmtInt(total)} — <Link href={`/models?org=${encodeURIComponent(org)}`} className="link">all models by this organization →</Link>81 </Note>82 )}83 </>84 );85}86function FamilyRows({ g }: { g: { label: string; href: string | null; items: ModelRow[] } }) {87 return (88 <>89 <tr className="bg-surface-2/60">90 <td colSpan={7} className="!py-1.5">91 <span className="eyebrow inline-flex items-center gap-2">92 {g.href ? (93 <Link href={g.href} className="hover:text-ink">94 {g.label}95 </Link>96 ) : (97 g.label98 )}99 <span className="tnum normal-case tracking-normal text-ink-3">{fmtInt(g.items.length)}</span>100 </span>101 </td>102 </tr>103 {g.items.map((m) => {104 const a = m.attributes ?? {};105 return (106 <tr key={m.id}>107 <Td primary>108 <EntityLink e={m} />109 </Td>110 <Td num label="Params" className="tnum">{fmtParams(a.parameter_count)}</Td>111 <Td num label="Context" className="tnum">{num(a.context_length) === null ? '—' : fmtTokens(a.context_length)}</Td>112 <Td label="Openness">{typeof a.openness === 'string' ? <OpennessBadge openness={a.openness} /> : <span className="text-ink-3">—</span>}</Td>113 <Td label="Status">114 <StatusBadge status={typeof a.status === 'string' ? a.status : m.status} />115 </Td>116 <Td label="Released" className="tnum text-ink-2">{typeof a.release_date === 'string' ? fmtDate(a.release_date) : '—'}</Td>117 <Td num label="Quality">118 <QualityMark q={m.quality?.score} />119 </Td>120 </tr>121 );122 })}123 </>124 );125}126127function FamiliesTable({ items }: { items: FamilyRow[] }) {128 if (!items.length) return <p className="text-sm text-ink-3">No family recorded for this organization.</p>;129 return (130 <DataTable caption="Families" compact>131 <thead>132 <tr>133 <Th>Family</Th>134 <Th num>Models</Th>135 <Th>First release</Th>136 <Th>Last release</Th>137 <Th>Modalities</Th>138 <Th>Canonical</Th>139 </tr>140 </thead>141 <tbody>142 {items.map((f) => (143 <tr key={f.slug}>144 <Td primary>145 {f.canonical ? (146 <Link href={routes.family(f.slug)} className="text-ink hover:text-accent hover:underline">147 {f.name}148 </Link>149 ) : (150 <span>{f.name}</span>151 )}152 </Td>153 <Td num label="Models" className="tnum">{fmtInt(f.model_count)}</Td>154 <Td label="First release" className="tnum text-ink-2">{fmtDate(f.first_release)}</Td>155 <Td label="Last release" className="tnum text-ink-2">{fmtDate(f.last_release)}</Td>156 <Td label="Modalities" className="text-xs text-ink-2">{f.modalities?.length ? f.modalities.join(', ') : '—'}</Td>157 <Td label="Canonical">{f.canonical ? <Chip tone="accent">entity</Chip> : <Chip>legacy label</Chip>}</Td>158 </tr>159 ))}160 </tbody>161 </DataTable>162 );163}164165function List({ items, empty }: { items: EntitySummary[]; empty: React.ReactNode }) {166 if (!items.length) return <p className="text-sm text-ink-3">{empty}</p>;167 return (168 <ul className="divide-y divide-rule border-y border-rule">169 {items.map((e) => (170 <li key={e.id} className="flex items-center gap-2 py-2 text-sm">171 <EntityBadge type={e.entity_type} small />172 <EntityLink e={e} className="truncate" />173 {typeof e.attributes?.published_at === 'string' && <span className="ml-auto shrink-0 text-xs text-ink-3">{fmtDate(e.attributes.published_at as string)}</span>}174 </li>175 ))}176 </ul>177 );178}179180export async function OrganizationPage({ d, canonical }: { d: EntityDetail; canonical: string }) {181 const a = d.attributes ?? {};182 const [models, families, papers, researchers, news, tl] = await Promise.all([183 safe(api.models({ org: d.slug, limit: 200, sort: 'release' })),184 safe(apiD1.families({ org: d.slug, limit: 50, sort: 'models' })),185 safe(api.papers({ org: d.slug, limit: 30, sort: 'published' })),186 safe(api.explore('researcher', { org: d.slug, limit: 30 })),187 safe(apiD3.changes({ entity: d.slug, type: 'ANNOUNCEMENT', include_backfill: 1, limit: 30 })),188 safe(apiD3.entityTimeline(d.slug, { include_backfill: 1, limit: 100 })),189 ]);190 const modelItems = (models?.items ?? []) as ModelRow[];191 const modelTotal = num(models?.total) ?? num(d.models?.total) ?? modelItems.length;192 const famItems = families?.items ?? [];193 const paperItems = papers?.items ?? d.papers ?? [];194 const researcherItems = (researchers?.items ?? []).filter((r) => r.entity_type === 'researcher');195 const withIds = researcherItems.filter((r) => (r.counts?.claims ?? 0) > 0 || (r.organization && r.organization.slug === d.slug));196 const providers = relItems(d, ['provider'], ['operates', 'owns']);197 const repos = [...(d.repositories ?? []), ...relItems(d, ['repository', 'framework', 'library'])].filter((x, i, arr) => arr.findIndex((y) => y.id === x.id) === i);198 const datasets = relItems(d, ['dataset']);199 const benchmarks = relItems(d, ['benchmark']);200 const newsItems: ChangeEvent[] = news?.items ?? [];201 const modelEvents: ChangeEvent[] = (tl?.items ?? []).filter((e) => !CORPORATE.has(e.event_type) && e.event_type !== 'ENTITY_MERGED');202 const corporateFromTimeline: ChangeEvent[] = (tl?.items ?? []).filter((e) => CORPORATE.has(e.event_type) && !newsItems.some((n) => n.id === e.id));203 const corporate = [...newsItems, ...corporateFromTimeline].sort((x, y) => ((y.occurred_at ?? y.observed_at) < (x.occurred_at ?? x.observed_at) ? -1 : 1));204 const link = ['website', 'official_url'].map((k) => a[k]).find((v): v is string => typeof v === 'string' && /^https?:\/\//.test(v)) ?? null;205 const crumbs = [{ name: SITE_NAME, href: '/' }, { name: 'Organizations', href: '/companies' }, { name: d.name, href: canonical }];206 const ld = { '@context': 'https://schema.org', '@type': 'Organization', name: d.name, url: `${SITE_URL}${canonical}`, description: d.description ?? undefined, alternateName: d.aliases?.length ? d.aliases : undefined, foundingDate: a.founded ? String(a.founded) : undefined, sameAs: link ? [link] : undefined, location: typeof a.headquarters === 'string' ? a.headquarters : undefined };207 const specRows = Object.keys(a)208 .filter((k) => !PROSE_KEYS.has(k) && !['founders', 'leadership'].includes(k))209 .map((k) => ({ key: k, raw: a[k] }));210 const sections = [211 { id: 'models', label: 'Models' },212 { id: 'families', label: 'Families' },213 { id: 'papers', label: 'Papers' },214 { id: 'researchers', label: 'Researchers' },215 { id: 'providers', label: 'Providers' },216 { id: 'repositories', label: 'Code' },217 { id: 'news', label: 'Corporate news' },218 { id: 'model-events', label: 'Model events' },219 { id: 'sources', label: 'Sources' },220 ];221222 return (223 <Container wide>224 <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(ld) }} />225 <BreadcrumbLd items={crumbs} />226 <ViewBeacon path={canonical} />227 <Breadcrumbs items={crumbs} />228 <header className="pb-5 pt-4 md:pb-6 md:pt-5">229 <div className="flex flex-wrap items-center gap-2">230 <EntityBadge type={d.entity_type} />231 {typeof a.org_kind === 'string' && a.org_kind.toLowerCase() !== typeLabel(d.entity_type).toLowerCase() && <Chip>{a.org_kind}</Chip>}232 {typeof a.country === 'string' && <Chip className="mono">{a.country}</Chip>}233 </div>234 <div className="mt-3 flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between">235 <div className="min-w-0">236 <h1 className="display text-[30px] md:text-[44px]">{d.name}</h1>237 <p className="mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-[15px] text-ink-2">238 {typeof a.headquarters === 'string' && <span>{a.headquarters}</span>}239 {a.founded ? <span>· founded {String(a.founded).slice(0, 4)}</span> : null}240 {link && (241 <a href={link} target="_blank" rel="noopener noreferrer" className="inline-flex items-center gap-1 text-ink-3 hover:text-accent">242 {link.replace(/^https?:\/\/(www\.)?/, '').replace(/\/$/, '').slice(0, 48)} <ExternalLink className="size-3.5" aria-hidden />243 </a>244 )}245 </p>246 {d.description && <p className="mt-3 max-w-3xl text-[15px] leading-relaxed text-ink-2">{d.description}</p>}247 <div className="mt-4 flex flex-wrap items-center gap-2" aria-label="Actions">248 <CompareButton e={d} />249 <WatchButton e={d} />250 <Link href={`/graph/${encodeURIComponent(d.slug)}?mode=company`} className="inline-flex h-9 items-center gap-1.5 border border-rule px-2.5 text-sm text-ink-2 hover:border-rule-strong hover:text-ink">251 <GitFork className="size-3.5" aria-hidden /> Company ecosystem graph252 </Link>253 <Link href={routes.timeline({ entity: d.slug })} className="inline-flex h-9 items-center gap-1.5 border border-rule px-2.5 text-sm text-ink-2 hover:border-rule-strong hover:text-ink">254 Timeline255 </Link>256 <Link href={routes.diff({ scope: `org:${d.slug}` })} className="inline-flex h-9 items-center gap-1.5 border border-rule px-2.5 text-sm text-ink-2 hover:border-rule-strong hover:text-ink">257 Diff (last 7 days)258 </Link>259 </div>260 </div>261 <div className="shrink-0 text-xs text-ink-3 lg:text-right">262 <QualityMark q={d.quality?.score} label />263 <p className="mt-1" title={d.updated_at}>264 Updated {fmtAgo(d.updated_at)} · first seen {fmtDate(d.first_seen_at)}265 </p>266 <p className="mono mt-0.5 text-[11px]">{d.id}</p>267 </div>268 </div>269 </header>270271 <DataStrip272 dense273 items={[274 { label: 'Models', value: fmtInt(modelTotal), definition: 'Canonical model releases attributed to this organization (artifacts and folded variants excluded).', href: '#models' },275 { label: 'Families', value: fmtInt(families?.total ?? famItems.length), definition: 'Model families whose official organization is this one.', href: '#families' },276 { label: 'Papers', value: fmtInt(papers?.total ?? paperItems.length), definition: 'Papers with this organization stated as publisher (arXiv metadata carries no affiliation, so this is often zero).', href: '#papers' },277 { label: 'Researchers', value: fmtInt(researchers?.total ?? researcherItems.length), definition: 'Researcher records affiliated with this organization by a stated relation.', href: '#researchers' },278 { label: 'Datasets', value: datasets.length ? fmtInt(datasets.length) : '—', definition: 'Datasets linked to this organization by a stated relation; the API has no per-organization dataset count.' },279 { label: 'Benchmarks', value: benchmarks.length ? fmtInt(benchmarks.length) : '—', definition: 'Benchmarks created by this organization (stated relation). Results by this organization’s models live on each leaderboard (?org=).' },280 { label: 'Providers', value: fmtInt(providers.length), definition: 'Inference providers operated or owned by this organization.', href: '#providers' },281 { label: 'Repositories', value: fmtInt(repos.length), definition: 'Repositories, frameworks and libraries linked by a stated relation.', href: '#repositories' },282 { label: 'Announcements', value: fmtInt(news?.total ?? corporate.length), definition: 'ANNOUNCEMENT events from this organization’s own news pages (including historical backfill).', href: '#news' },283 ]}284 />285 <SectionNav items={sections} className="mt-3" />286287 <div className="grid gap-10 pb-16 lg:grid-cols-[minmax(0,1fr)_22rem]">288 <div className="min-w-0">289 <Section id="models" eyebrow="Models" title={<>Models <span className="tnum text-base font-normal text-ink-3">{fmtInt(modelTotal)}</span></>} lede="Grouped by family (canonical family entity when known, legacy label otherwise)." action={{ href: `/models?org=${encodeURIComponent(d.slug)}`, label: 'Filter in Models' }}>290 <ModelsByFamily items={modelItems} total={modelTotal} org={d.slug} />291 </Section>292 <Section id="families" eyebrow="Families" title={<>Families <span className="tnum text-base font-normal text-ink-3">{fmtInt(families?.total ?? famItems.length)}</span></>}>293 <FamiliesTable items={famItems} />294 </Section>295 <Section id="papers" eyebrow="Research" title={<>Papers <span className="tnum text-base font-normal text-ink-3">{fmtInt(papers?.total ?? paperItems.length)}</span></>} action={{ href: `/papers?org=${encodeURIComponent(d.slug)}`, label: 'All' }}>296 <List items={paperItems} empty={<>No paper is attributed to {d.name}: arXiv metadata carries author names but no affiliation, and a paper is linked to an organization only when an official model card or lab page cites it.</>} />297 </Section>298 <Section id="researchers" eyebrow="People" title={<>Researchers <span className="tnum text-base font-normal text-ink-3">{fmtInt(withIds.length)}</span></>} lede="Only researcher records with an identifier or a stated affiliation are listed.">299 <List items={withIds} empty={<>No researcher is affiliated with {d.name} by a stated relation. The atlas's researcher rows come from arXiv author lists (name only, no identifier, no affiliation) and are not attributed to organizations.</>} />300 {researcherItems.length > withIds.length && <Note className="mt-2">{fmtInt(researcherItems.length - withIds.length)} name-only legacy rows omitted.</Note>}301 </Section>302 <Section id="providers" eyebrow="Serving" title={<>Providers operated <span className="tnum text-base font-normal text-ink-3">{fmtInt(providers.length)}</span></>}>303 <List items={providers} empty="No provider operated by this organization is recorded." />304 </Section>305 <Section id="repositories" eyebrow="Code" title={<>Repositories & frameworks <span className="tnum text-base font-normal text-ink-3">{fmtInt(repos.length)}</span></>}>306 <List items={repos} empty="No repository or framework linked." />307 </Section>308 <Section id="news" eyebrow="Timeline" title={<>Corporate news <span className="tnum text-base font-normal text-ink-3">{fmtInt(news?.total ?? corporate.length)}</span></>} lede="ANNOUNCEMENT events from the organization's own channels, newest first (dates are publication dates when stated).">309 {corporate.length === 0 ? (310 <EmptyState title="No announcement recorded">The news connector for this organization has not produced announcements yet.</EmptyState>311 ) : (312 <ul className="border-t border-rule">313 {corporate.slice(0, 30).map((e) => (314 <ChangeRow key={e.id} e={e} showDate live={false} />315 ))}316 </ul>317 )}318 </Section>319 <Section id="model-events" eyebrow="Timeline" title={<>Model events <span className="tnum text-base font-normal text-ink-3">{fmtInt(modelEvents.length)}</span></>} lede="New models, releases, deprecations, price and property changes for the models this organization develops." action={{ href: routes.timeline({ entity: d.slug }), label: 'Full timeline' }}>320 {modelEvents.length === 0 ? (321 <EmptyState title="No model event recorded" />322 ) : (323 <ul className="border-t border-rule">324 {modelEvents.slice(0, 40).map((e) => (325 <ChangeRow key={e.id} e={e} showDate live={false} />326 ))}327 </ul>328 )}329 {modelEvents.length > 40 && <Note className="mt-2">First 40 of {fmtInt(modelEvents.length)} loaded — the full timeline has cursor paging.</Note>}330 </Section>331 <Section id="sources" eyebrow="Sources" title={<>Sources <span className="tnum text-base font-normal text-ink-3">{fmtInt(d.sources?.length ?? 0)}</span></>}>332 <SourcesTable sources={d.sources ?? []} />333 </Section>334 </div>335 <aside className="min-w-0 space-y-8 lg:pt-8">336 <section>337 <p className="eyebrow mb-2 inline-flex items-center gap-1">338 Record <Hint align="right" text="Every value shows its source, tier and observation time; click a value for the claim behind it." />339 </p>340 <KeyValue rows={specRows} provenance={d.provenance} slug={d.slug} entity={{ name: d.name, entity_type: d.entity_type }} dense />341 </section>342 <section>343 <p className="eyebrow mb-2">Identity</p>344 <Identity d={d} />345 <p className="mono mt-2 break-all text-[11px] text-ink-3">slug {d.slug}</p>346 </section>347 <section>348 <p className="eyebrow mb-2">Definition</p>349 <p className="text-xs text-ink-3">Organizations = companies, labs and universities ({ORG_TYPES.join(', ')}). Counts on this page are live from the listing APIs and the stated relations of this record.</p>350 </section>351 </aside>352 </div>353 </Container>354 );355}356