SPB Git forge

spb/ai-atlas

Public
41commits 1branches 0releases
4.6 MBsize
maindefault branch
12 days agolast push
HTML 77.2% TypeScript 10.5% Python 9.6% JavaScript 2.5%
15.1 KB · 251 lines tsx
Raw Blame History
1import type { Metadata } from 'next';2import Link from 'next/link';3import { Sparkline } from '@/components/charts';4import { ChangeRow } from '@/components/changes/change-row';5import { Methodology, TrustChip } from '@/components/intelligence/bits';6import { DataStrip, type StripItem } from '@/components/layout/terminal';7import { PriceMovers } from '@/components/prices/movers';8import { ChipRow } from '@/components/timeline/chip-row';9import { DataTable, EmptyRow, Td, Th } from '@/components/ui/data-table';10import { EntityLink } from '@/components/ui/entity';11import { Container, Note, PageHeader, Section } from '@/components/ui/section';12import { EmptyState, Unavailable } from '@/components/ui/unavailable';13import { api, intel, safe } from '@/lib/api';14import { fmtDate, fmtDateTime, fmtInt, fmtScore, fmtTokens, num } from '@/lib/format';15import { routes, SITE_NAME, SITE_URL } from '@/lib/site';16import type { ChangeEvent, EntitySummary, PulseLeaderItem } from '@/lib/types';1718export const revalidate = 120;1920const WINDOWS = [7, 30, 90];21type SP = Record<string, string | undefined>;22const pickDays = (sp: SP) => (WINDOWS.includes(Number(sp.days)) ? Number(sp.days) : 7);2324const LABELS: Record<string, string> = {25  new_models: 'New models',26  new_open_weight_models: 'New open-weight models',27  new_artifacts: 'New artifacts',28  new_papers: 'New papers',29  provider_listings: 'Provider listings',30  provider_delistings: 'Delistings',31  price_changes: 'Price changes',32  new_models_1m_context: 'New ≥ 1M-context models',33  new_benchmark_leaders: 'New benchmark leaders',34  documents_changed: 'Documents changed',35  sources_observed: 'Sources observed',36  events_total: 'Events total',37};38const HREF: Record<string, (days: number) => string> = {39  new_models: () => `${routes.changes()}?type=NEW_MODEL`,40  new_open_weight_models: () => `${routes.open()}`,41  new_papers: () => `${routes.papers()}?sort=published`,42  provider_listings: () => `${routes.changes()}?type=PROVIDER_LISTED`,43  provider_delistings: () => `${routes.changes()}?type=PROVIDER_DELISTED`,44  price_changes: () => `${routes.prices()}`,45  new_benchmark_leaders: () => routes.benchmarks(),46  documents_changed: () => `${routes.changes()}?type=DOCUMENT_CHANGED&include_documents=1`,47  sources_observed: () => routes.sources(),48  events_total: () => routes.changes(),49};50/** stats/history series key per counter (only where the history payload carries one). */51const HISTORY_KEY: Record<string, (c: Record<string, unknown>) => number | null> = {52  new_models: (c) => num((c.entities as Record<string, unknown> | undefined)?.model),53  new_papers: (c) => num((c.entities as Record<string, unknown> | undefined)?.paper),54  price_changes: (c) => num(c.prices_current),55  events_total: (c) => num(c.change_events),56  documents_changed: (c) => num(c.documents),57  sources_observed: (c) => num(c.sources),58};5960const TITLE = 'Ecosystem Pulse — what happened in AI this week';61const DESC = 'Deterministic counters over events that occurred in the window (7, 30 or 90 days) and are not back-filled history: new models, open-weight releases, artifacts, papers, provider listings and delistings, price changes, new 1M-context models, new benchmark leaders, documents and sources observed — each with its definition.';62export async function generateMetadata({ searchParams }: { searchParams: Promise<SP> }): Promise<Metadata> {63  const days = pickDays(await searchParams);64  const title = days === 7 ? TITLE : `Ecosystem Pulse — the last ${days} days in AI`;65  return { title, description: DESC, alternates: { canonical: routes.pulse() }, openGraph: { title: `${title} | ${SITE_NAME}`, description: DESC, url: `${SITE_URL}${routes.pulse()}`, type: 'website', siteName: SITE_NAME }, twitter: { card: 'summary_large_image', title, description: DESC }, robots: days !== 7 ? { index: false, follow: true } : undefined };66}6768const isLeader = (x: unknown): x is PulseLeaderItem => !!x && typeof x === 'object' && 'benchmark' in (x as object);69const isEvent = (x: unknown): x is ChangeEvent => !!x && typeof x === 'object' && 'event_type' in (x as object);70const isModel = (x: unknown): x is EntitySummary => !!x && typeof x === 'object' && 'entity_type' in (x as object) && 'slug' in (x as object);7172export default async function PulsePage({ searchParams }: { searchParams: Promise<SP> }) {73  const days = pickDays(await searchParams);74  const since = new Date(Date.now() - days * 86400000).toISOString().slice(0, 10);75  const [pulse, history, priceFallback, ctxFallback] = await Promise.all([safe(intel.pulse(days)), safe(api.statsHistory(Math.max(days, 14))), safe(api.changes({ type: 'PRICE_CHANGED', since, limit: 30 })), safe(api.models({ min_context: 1_000_000, sort: 'release', limit: 12 }))]);76  const href = (d: number) => (d === 7 ? routes.pulse() : `${routes.pulse()}?days=${d}`);7778  if (!pulse) {79    return (80      <Container wide>81        <PageHeader eyebrow="Ecosystem pulse" title="Pulse" lede="Deterministic counters over what occurred in the window." />82        <Unavailable what="Pulse" reason="GET /pulse did not answer. Counters are never cached or hardcoded." />83      </Container>84    );85  }86  const counters = pulse.counters ?? {};87  const hist = history?.items ?? [];88  const spark = (key: string): number[] => {89    const f = HISTORY_KEY[key];90    if (!f) return [];91    return hist.map((h) => f(h.counts as Record<string, unknown>)).filter((v): v is number => v !== null);92  };93  const strip: StripItem[] = Object.entries(counters).map(([key, c]) => {94    const vals = spark(key);95    return {96      label: LABELS[key] ?? key.replace(/_/g, ' '),97      value: fmtInt(c.value),98      definition: c.definition,99      href: HREF[key]?.(days),100      hint: vals.length >= 2 ? <Sparkline values={vals} width={64} height={16} stroke="var(--accent)" title={`${LABELS[key] ?? key} · daily stats history`} /> : num(c.median_percent) !== null ? `median ${fmtScore(c.median_percent)} %` : undefined,101    };102  });103  const leaders = (counters.new_benchmark_leaders?.items ?? []).filter(isLeader);104  const priceItems = (counters.price_changes?.items ?? []).filter(isEvent);105  const priceRows = priceItems.length ? priceItems : (priceFallback?.items ?? []);106  const ctxItems = (counters.new_models_1m_context?.items ?? []);107  const ctxModels: EntitySummary[] = ctxItems.filter(isModel);108  const ctxEvents: ChangeEvent[] = ctxItems.filter(isEvent);109  const historyDays = hist.length;110  const ld = { '@context': 'https://schema.org', '@type': 'Dataset', name: `AI Atlas Pulse · ${days} days`, description: DESC, url: `${SITE_URL}${routes.pulse()}`, temporalCoverage: `${pulse.since}/${pulse.until}`, creator: { '@type': 'Organization', name: SITE_NAME, url: SITE_URL } };111112  return (113    <Container wide>114      <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(ld) }} />115      <PageHeader eyebrow="Ecosystem pulse" title={`The last ${days} days in AI`} lede="Deterministic counters over events that occurred in the window — historical backfill excluded, so a re-crawl of old pages never inflates them. Hover a label for how each one is counted." aside={<p className="tnum text-sm text-ink-3" title={`${pulse.since} → ${pulse.until}`}>{fmtDate(pulse.since)} → {fmtDateTime(pulse.until)}</p>}>116        <div className="mt-6">117          <p className="eyebrow mb-1.5">Window</p>118          <ChipRow label="Window" items={WINDOWS.map((d) => ({ href: href(d), label: `${d} days`, active: days === d }))} />119        </div>120      </PageHeader>121122      <DataStrip items={strip} />123      <p className="tnum mt-2 text-xs text-ink-3">124        All counters use <span className="mono">is_backfill = false</span> and <span className="mono">occurred_at</span> inside the window. Sparklines show the daily stats history where a series exists ({historyDays} day{historyDays === 1 ? '' : 's'} recorded so far{historyDays < 2 ? ' — a line needs two' : ''}).125      </p>126      <Methodology text={pulse.note} className="mt-1" />127128      {/* --------------------------------------------------------------------------------------------- leaders */}129      <Section eyebrow="Benchmark leadership" title={`New benchmark leaders · ${fmtInt(counters.new_benchmark_leaders?.value)}`} lede={counters.new_benchmark_leaders?.definition} action={{ href: routes.benchmarks(), label: 'All leaderboards' }}>130        {leaders.length === 0 ? (131          <EmptyState title="No leadership change in the window">The primary-group leader of every benchmark is unchanged over these {days} days.</EmptyState>132        ) : (133          <div className="md:overflow-x-auto">134          <DataTable caption="New benchmark leaders">135            <thead>136              <tr>137                <Th>Benchmark</Th>138                <Th>Current leader</Th>139                <Th num>Score</Th>140                <Th>Previous leader</Th>141                <Th>Group</Th>142                <Th>Trust</Th>143              </tr>144            </thead>145            <tbody>146              {leaders.map((l) => (147                <tr key={l.benchmark.id}>148                  <Td primary>149                    <Link href={routes.benchmark(l.benchmark.slug)} className="text-ink hover:text-accent hover:underline">150                      {l.benchmark.name}151                    </Link>152                  </Td>153                  <Td label="Current leader">154                    {l.current ? (155                      <>156                        <Link href={routes.entity({ entity_type: l.current.model.entity_type ?? 'model', slug: l.current.model.slug })} className="font-medium text-ink hover:text-accent hover:underline">157                          {l.current.model.name}158                        </Link>159                        {l.current.model.organization && <span className="ml-1.5 text-xs text-ink-3">{l.current.model.organization.name}</span>}160                      </>161                    ) : (162                      '—'163                    )}164                  </Td>165                  <Td num label="Score" className="tnum font-medium">{l.current ? `${fmtScore(l.current.score)}${l.current.metric ? ` ${l.current.metric}` : ''}` : '—'}</Td>166                  <Td label="Previous leader" className="text-ink-2">167                    {l.previous ? (168                      <>169                        <Link href={routes.entity({ entity_type: l.previous.model.entity_type ?? 'model', slug: l.previous.model.slug })} className="hover:text-accent">170                          {l.previous.model.name}171                        </Link>{' '}172                        <span className="tnum text-xs text-ink-3">{fmtScore(l.previous.score)}</span>173                      </>174                    ) : (175                      <span className="text-ink-3">first leader recorded</span>176                    )}177                  </Td>178                  <Td label="Group" className="mono text-[11px] text-ink-3">179                    {l.current?.group_label ?? '—'}180                    {num(l.current?.n_models) !== null && <span> · n={fmtInt(l.current?.n_models)}</span>}181                  </Td>182                  <Td label="Trust"><TrustChip level={l.current?.trust_level} /></Td>183                </tr>184              ))}185            </tbody>186          </DataTable>187          </div>188        )}189      </Section>190191      {/* ---------------------------------------------------------------------------------------------- prices */}192      <Section eyebrow="Prices" title={`Price changes · ${fmtInt(counters.price_changes?.value)}${num(counters.price_changes?.median_percent) !== null ? ` · median ${fmtScore(counters.price_changes?.median_percent)} %` : ''}`} lede={counters.price_changes?.definition} action={{ href: routes.prices(), label: 'Price terminal' }}>193        {!priceItems.length && priceRows.length > 0 && <Note className="mb-2">The pulse returns the count only; the rows below are PRICE_CHANGED events since {fmtDate(since)} from the change feed (same window, same backfill rule).</Note>}194        <PriceMovers movers={priceRows} limit={30} />195      </Section>196197      {/* --------------------------------------------------------------------------------------------- context */}198      <Section eyebrow="Long context" title={`New ≥ 1M-context models · ${fmtInt(counters.new_models_1m_context?.value)}`} lede={counters.new_models_1m_context?.definition}>199        {ctxModels.length > 0 ? (200          <ul className="border-t border-rule">201            {ctxModels.map((m) => (202              <li key={m.id} className="flex flex-wrap items-baseline gap-x-3 border-b border-rule py-2.5 text-sm">203                <EntityLink e={m} className="font-medium" />204                {m.organization && <span className="text-xs text-ink-3">{m.organization.name}</span>}205                <span className="tnum ml-auto text-xs text-ink-2">{fmtTokens(m.attributes?.context_length)} · {typeof m.attributes?.release_date === 'string' ? fmtDate(m.attributes.release_date) : '—'}</span>206              </li>207            ))}208          </ul>209        ) : ctxEvents.length > 0 ? (210          <ul className="border-t border-rule">{ctxEvents.map((e) => <ChangeRow key={e.id} e={e} showDate live={false} />)}</ul>211        ) : num(counters.new_models_1m_context?.value) === 0 ? (212          <>213            <EmptyState title={`No new model with a ≥ 1M-token context in the last ${days} days`}>214              {ctxFallback?.items.length ? <>For reference, the most recently released models with a sourced context of at least 1M tokens (any date):</> : null}215            </EmptyState>216            {ctxFallback?.items.length ? (217              <ul className="border-b border-rule">218                {ctxFallback.items.slice(0, 8).map((m) => (219                  <li key={m.id} className="flex flex-wrap items-baseline gap-x-3 border-b border-rule py-2 text-sm last:border-b-0">220                    <EntityLink e={m} />221                    {m.organization && <span className="text-xs text-ink-3">{m.organization.name}</span>}222                    <span className="tnum ml-auto text-xs text-ink-2">{fmtTokens(m.attributes?.context_length)} · {typeof m.attributes?.release_date === 'string' ? fmtDate(m.attributes.release_date) : 'release date unavailable'}</span>223                  </li>224                ))}225              </ul>226            ) : null}227          </>228        ) : (229          <EmptyState title={`${fmtInt(counters.new_models_1m_context?.value)} new ≥ 1M-context models`}>The pulse returns the count only; the list is not part of the API response yet.</EmptyState>230        )}231      </Section>232233      <Section eyebrow="Reading the pulse" title="Definitions" hairline>234        <dl className="grid gap-x-8 gap-y-3 md:grid-cols-2">235          {Object.entries(counters).map(([k, c]) => (236            <div key={k} className="border-b border-rule pb-2">237              <dt className="flex items-baseline justify-between gap-3 text-sm font-medium text-ink">238                {LABELS[k] ?? k.replace(/_/g, ' ')} <span className="tnum text-ink-2">{fmtInt(c.value)}</span>239              </dt>240              <dd className="mt-0.5 text-xs leading-relaxed text-ink-3">{c.definition}</dd>241            </div>242          ))}243        </dl>244        <Note className="mt-3">245          Source: <Link href="/developers" className="link">GET /pulse?days={days}</Link>. Nothing on this page is a projection.246        </Note>247      </Section>248    </Container>249  );250}251