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%
26.3 KB · 440 lines tsx
Raw Blame History
1import type { Metadata } from 'next';2import Link from 'next/link';3import { Fragment } from 'react';4import { ChangeRow } from '@/components/changes/change-row';5import { BTN_GHOST, CTRL, DistBars, Field, Methodology, SortTh } from '@/components/intelligence/bits';6import { ExpandableOfferRow } from '@/components/intelligence/expand-price-row';7import { PriceIndexChart } from '@/components/intelligence/price-index-chart';8import { DataStrip, type StripItem, TerminalLayout } from '@/components/layout/terminal';9import { PriceMovers } from '@/components/prices/movers';10import { Chip } from '@/components/ui/badges';11import { DataTable, EmptyRow, Td, Th } from '@/components/ui/data-table';12import { EntityLink } from '@/components/ui/entity';13import { Pagination, withParams } from '@/components/ui/pagination';14import { SourceCell } from '@/components/ui/provenance';15import { Note, PageHeader, Section } from '@/components/ui/section';16import { EmptyState, Unavailable } from '@/components/ui/unavailable';17import { api, ApiError, intel, safe } from '@/lib/api';18import { fmtAgo, fmtDate, fmtInt, fmtTokens, fmtUsdPerM, num } from '@/lib/format';19import { routes, SITE_NAME, SITE_URL } from '@/lib/site';20import type { CheapestFrontier, Page, Price } from '@/lib/types';2122export const revalidate = 300;2324type SP = Record<string, string | undefined>;25const LIMIT = 100;26const DAYS = [30, 90, 180, 365];27const SORTS = ['input', 'output', 'cheapest_frontier', 'model', 'provider', 'observed'] as const;28type Sort = (typeof SORTS)[number];29const KEYS = ['days', 'scale', 'sort', 'provider', 'org', 'family', 'modality', 'model', 'offset'] as const;30const SORT_LABELS: Record<Sort, string> = { input: 'Cheapest input', output: 'Cheapest output', cheapest_frontier: 'Cheapest frontier output', model: 'Model', provider: 'Provider', observed: 'Recently observed' };3132function pick(sp: SP) {33  const cur: Record<string, string | undefined> = {};34  for (const k of KEYS) if (sp[k]) cur[k] = sp[k];35  const days = DAYS.includes(Number(cur.days)) ? Number(cur.days) : 180;36  const sort: Sort = (SORTS as readonly string[]).includes(cur.sort ?? '') ? (cur.sort as Sort) : 'input';37  return { cur, days, sort, offset: Math.max(0, Number(cur.offset) || 0), scale: cur.scale === 'log' ? ('log' as const) : ('linear' as const) };38}3940const TITLE = 'AI Price Index — USD per 1M tokens, every provider, every day';41export async function generateMetadata({ searchParams }: { searchParams: Promise<SP> }): Promise<Metadata> {42  const { cur, days } = pick(await searchParams);43  const filtered = cur.provider || cur.model || cur.org || cur.family || cur.modality;44  const title = filtered ? `AI prices — ${[cur.provider, cur.org, cur.family, cur.modality, cur.model].filter(Boolean).join(' · ')}` : TITLE;45  const description = `Daily medians of published input, output, frontier, open-weight and embedding prices per 1M tokens across every provider offer AI Atlas tracks (last ${days} days), the distribution of current offers, the cheapest frontier model, price movers, new listings and delistings, and every current offer with its source and history.`;46  return {47    title,48    description,49    alternates: { canonical: routes.prices() },50    openGraph: { title: `${title} | ${SITE_NAME}`, description, url: `${SITE_URL}${routes.prices()}`, type: 'website', siteName: SITE_NAME },51    twitter: { card: 'summary_large_image', title, description },52    robots: filtered || cur.offset ? { index: false, follow: true } : undefined,53  };54}5556/** `/prices` 404s when a slug filter is unknown — keep that distinction instead of a generic "unavailable". */57async function loadPrices(query: Record<string, string | number | undefined>): Promise<{ page: (Page<Price> & { methodology?: string }) | null; notFound: boolean; detail?: string | null }> {58  try {59    return { page: await intel.prices(query), notFound: false };60  } catch (e) {61    return { page: null, notFound: e instanceof ApiError && e.notFound, detail: e instanceof ApiError ? e.detail : null };62  }63}6465function CheapestStrip({ c, label }: { c: CheapestFrontier | null | undefined; label: string }) {66  return (67    <div className="min-w-0 py-3">68      <p className="eyebrow">{label}</p>69      {c ? (70        <>71          <p className="mt-1 flex flex-wrap items-baseline gap-x-2">72            <EntityLink e={c.model} className="text-[15px] font-medium" />73            {c.model.organization && <span className="text-xs text-ink-3">{c.model.organization.name}</span>}74          </p>75          <p className="tnum mt-1 text-[22px] font-semibold leading-none text-accent-2">76            {fmtUsdPerM(c.output)} <span className="text-xs font-normal text-ink-3">output / 1M</span>77          </p>78          <p className="tnum mt-1 text-xs text-ink-3">79            input {fmtUsdPerM(c.input)} · context {fmtTokens(c.context_length)} · via <EntityLink e={c.provider} className="text-ink-2" />80          </p>81        </>82      ) : (83        <p className="mt-1 text-sm text-ink-3">No frontier model with a current priced offer in this response.</p>84      )}85    </div>86  );87}8889export default async function PricesPage({ searchParams }: { searchParams: Promise<SP> }) {90  const sp = await searchParams;91  const { cur, days, sort, offset, scale } = pick(sp);92  const [index, providers, facets, priced, listed, delisted] = await Promise.all([93    safe(intel.priceIndex(days)),94    safe(intel.providers()),95    safe(api.models({ limit: 1, facets: 1 })),96    loadPrices({ sort, provider: cur.provider, model: cur.model, org: cur.org, family: cur.family, modality: cur.modality, limit: LIMIT, offset }),97    safe(api.changes({ type: 'PROVIDER_LISTED', limit: 8 })),98    safe(api.changes({ type: 'PROVIDER_DELISTED', limit: 8 })),99  ]);100  const href = (patch: Record<string, string | number | undefined | null>) => withParams('/prices', cur, patch);101  const providerHref = (name: string) => {102    const p = (providers?.items ?? []).find((x) => x.name === name);103    return p ? href({ provider: p.slug, offset: undefined }) : undefined;104  };105106  // ---- index107  const series = index?.series ?? [];108  const populated = series.filter((p) => num(p.median_input) !== null || num(p.median_output) !== null);109  const latest = populated.at(-1) ?? null;110  const first = populated[0] ?? null;111  const delta = (field: 'median_input' | 'median_output' | 'median_frontier_output' | 'median_open_output' | 'median_embedding_input') => {112    const a = num(first?.[field]);113    const b = num(latest?.[field]);114    if (a === null || b === null || a === 0 || first === latest) return undefined;115    const pct = ((b - a) / a) * 100;116    if (Math.abs(pct) < 0.05) return undefined;117    return { value: `${pct > 0 ? '+' : ''}${pct.toFixed(1)}%`, tone: pct < 0 ? ('positive' as const) : pct > 0 ? ('negative' as const) : ('neutral' as const) };118  };119  const money = (v: unknown) => <span className="text-accent-2">{fmtUsdPerM(v)}</span>;120  const strip: StripItem[] | null = latest121    ? [122        { label: 'Median input', value: money(latest.median_input), definition: 'Median of live input prices (USD / 1M tokens) across every provider offer valid at the end of the day; zero or missing prices excluded.', delta: delta('median_input'), hint: `n=${fmtInt(latest.sample?.offers ?? latest.offers)}` },123        { label: 'Median output', value: money(latest.median_output), definition: 'Median of live output prices across every provider offer valid at the end of the day.', delta: delta('median_output') },124        { label: 'Median frontier output', value: money(latest.median_frontier_output), definition: index?.frontier?.methodology ?? 'Frontier models = recent releases by active organizations or top-10 on a benchmark; no composite score.', delta: delta('median_frontier_output'), hint: `n=${fmtInt(latest.sample?.frontier_offers)}` },125        { label: 'Median open output', value: money(latest.median_open_output), definition: 'Median output price over models with openness open-weights / open-source.', delta: delta('median_open_output'), hint: `n=${fmtInt(latest.sample?.open_models)}` },126        { label: 'Median embedding input', value: money(latest.median_embedding_input), definition: 'Median input price over models whose modalities include embedding.', delta: delta('median_embedding_input'), hint: `n=${fmtInt(latest.sample?.embedding_models)}` },127        { label: 'Models priced', value: fmtInt(latest.models), definition: 'Canonical models with at least one live offer on the latest day.', hint: fmtDate(latest.day) },128        { label: 'Cheapest input', value: fmtUsdPerM(latest.min_input), definition: 'Lowest positive live input price on the latest day.', hint: num(latest.max_input) !== null ? `dearest ${fmtUsdPerM(latest.max_input)}` : undefined },129      ]130    : null;131132  // ---- offers133  const page = priced.page;134  const rows = page?.items ?? [];135  const minByModel = new Map<string, number>();136  const providersByModel = new Map<string, Set<string>>();137  for (const r of rows) {138    const v = num(r.input_per_mtok);139    if (v !== null) {140      const m = minByModel.get(r.model.slug);141      if (m === undefined || v < m) minByModel.set(r.model.slug, v);142    }143    const s = providersByModel.get(r.model.slug) ?? new Set<string>();144    s.add(r.provider.slug);145    providersByModel.set(r.model.slug, s);146  }147  const providerOptions = (providers?.items ?? []).slice().sort((a, b) => a.name.localeCompare(b.name));148  const providerName = providerOptions.find((o) => o.slug === cur.provider)?.name;149  const orgOptions = ((facets?.facets as { organizations?: { slug: string; name: string; count?: unknown }[] } | undefined)?.organizations ?? []).slice(0, 60);150  const familyOptions = ((facets?.facets as { families?: { value: string; label?: string; count?: unknown }[] } | undefined)?.families ?? []).slice(0, 60);151  const modalityOptions = ((facets?.facets as { modalities?: { value: string; count?: unknown }[] } | undefined)?.modalities ?? []);152  const filterCount = ['provider', 'org', 'family', 'modality', 'model'].filter((k) => cur[k]).length + (days !== 180 ? 1 : 0);153  const newListings = index?.new_listings_30d;154  const delistings = index?.delistings_30d;155  const changes30 = index?.price_changes_30d;156  const asCount = (v: unknown) => (Array.isArray(v) ? v.length : num(v));157158  const filters = (159    <form action="/prices" method="get" className="space-y-3" data-price-filters>160      <Field label="Provider">161        <select name="provider" defaultValue={cur.provider ?? ''} className={CTRL}>162          <option value="">Any provider</option>163          {providerOptions.map((p) => (164            <option key={p.slug} value={p.slug}>165              {p.name}166            </option>167          ))}168        </select>169      </Field>170      <Field label="Organization">171        <select name="org" defaultValue={cur.org ?? ''} className={CTRL}>172          <option value="">Any organization</option>173          {orgOptions.map((o) => (174            <option key={o.slug} value={o.slug}>175              {o.name}176            </option>177          ))}178          {cur.org && !orgOptions.some((o) => o.slug === cur.org) && <option value={cur.org}>{cur.org}</option>}179        </select>180      </Field>181      <Field label="Family">182        <select name="family" defaultValue={cur.family ?? ''} className={CTRL}>183          <option value="">Any family</option>184          {familyOptions.map((f) => (185            <option key={f.value} value={f.value}>186              {f.label ?? f.value}187            </option>188          ))}189          {cur.family && !familyOptions.some((f) => f.value === cur.family) && <option value={cur.family}>{cur.family}</option>}190        </select>191      </Field>192      <Field label="Modality">193        <select name="modality" defaultValue={cur.modality ?? ''} className={CTRL}>194          <option value="">Any modality</option>195          {modalityOptions.map((m) => (196            <option key={m.value} value={m.value}>197              {m.value}198            </option>199          ))}200        </select>201      </Field>202      <Field label="Model slug" hint="Slugs only — the API 404s on free text.">203        <input name="model" defaultValue={cur.model ?? ''} placeholder="e.g. claude-opus-5" className={CTRL} />204      </Field>205      <Field label="Index window">206        <select name="days" defaultValue={String(days)} className={CTRL}>207          {DAYS.map((d) => (208            <option key={d} value={d}>209              Last {d} days210            </option>211          ))}212        </select>213      </Field>214      <Field label="Sort offers">215        <select name="sort" defaultValue={sort} className={CTRL}>216          {SORTS.map((s) => (217            <option key={s} value={s}>218              {SORT_LABELS[s]}219            </option>220          ))}221        </select>222      </Field>223      {cur.scale && <input type="hidden" name="scale" value={cur.scale} />}224      <div className="flex gap-2">225        <button type="submit" className="inline-flex h-11 flex-1 items-center justify-center bg-ink lg:h-10 px-3 text-sm font-medium text-canvas hover:opacity-90">226          Apply227        </button>228        <Link href="/prices" className={BTN_GHOST}>229          Reset230        </Link>231      </div>232    </form>233  );234235  const inspector = (236    <div className="space-y-4 text-xs leading-relaxed text-ink-2">237      <div>238        <p className="eyebrow mb-1">Index method</p>239        <p>{index?.methodology ?? index?.note ?? 'Unavailable.'}</p>240      </div>241      {latest?.sample && (242        <div>243          <p className="eyebrow mb-1">Sample · {fmtDate(latest.day)}</p>244          <dl className="tnum grid grid-cols-[auto_1fr] gap-x-3 gap-y-0.5">245            <dt className="text-ink-3">offers</dt>246            <dd>{fmtInt(latest.sample.offers)}</dd>247            <dt className="text-ink-3">models</dt>248            <dd>{fmtInt(latest.sample.models)}</dd>249            <dt className="text-ink-3">frontier</dt>250            <dd>251              {fmtInt(latest.sample.frontier_models)} models · {fmtInt(latest.sample.frontier_offers)} offers252            </dd>253            <dt className="text-ink-3">open</dt>254            <dd>{fmtInt(latest.sample.open_models)} models</dd>255            <dt className="text-ink-3">embedding</dt>256            <dd>{fmtInt(latest.sample.embedding_models)} models</dd>257          </dl>258        </div>259      )}260      {index?.frontier?.composition && (261        <div>262          <p className="eyebrow mb-1">Frontier composition</p>263          <dl className="tnum grid grid-cols-[auto_1fr] gap-x-3 gap-y-0.5">264            {Object.entries(index.frontier.composition).map(([k, v]) => (265              <Fragment key={k}>266                <dt className="text-ink-3">{k.replace(/_/g, ' ')}</dt>267                <dd>{typeof v === 'string' && /^\d{4}-/.test(v) ? fmtDate(v) : fmtInt(v)}</dd>268              </Fragment>269            ))}270          </dl>271        </div>272      )}273      <div>274        <p className="eyebrow mb-1">30-day counters</p>275        <dl className="tnum grid grid-cols-[auto_1fr] gap-x-3 gap-y-0.5">276          <dt className="text-ink-3">new listings</dt>277          <dd>{fmtInt(asCount(newListings))}</dd>278          <dt className="text-ink-3">delistings</dt>279          <dd>{fmtInt(asCount(delistings))}</dd>280          <dt className="text-ink-3">price changes</dt>281          <dd>{fmtInt(asCount(changes30))}</dd>282        </dl>283      </div>284      <p>285        <Link href="/developers" className="link">GET /prices/index</Link> · <Link href="/methodology" className="link">methodology</Link>286      </p>287    </div>288  );289290  return (291    <TerminalLayout filters={filters} inspector={inspector} filtersTitle="Filters" inspectorTitle="Method" storageKey="aia-inspector-prices" filterCount={filterCount}>292      <PageHeader eyebrow="Price history terminal" title="AI Price Index" lede="What a million tokens costs, day by day, across every provider offer the atlas tracks — medians for the whole market, the frontier, open weights and embeddings; the distribution of current offers; movers, listings and every offer with its history." aside={latest ? <p className="tnum text-sm text-ink-3">{fmtInt(latest.offers)} offers · {fmtInt(latest.models)} models · {fmtDate(latest.day)}</p> : undefined} className="pt-4 md:pt-6" />293294      {/* ------------------------------------------------------------------------------------------------ index */}295      <Section eyebrow="AI Price Index" title={<>Daily medians · last {days} days</>} hairline={false} className="pt-0">296        {!index ? (297          <Unavailable what="Price index" />298        ) : !latest ? (299          <EmptyState title="No priced offers in this window">Prices appear once a provider pricing page has been crawled. Try a longer window.</EmptyState>300        ) : (301          <>302            {strip && <DataStrip items={strip} dense />}303            <PriceIndexChart series={series} initialScale={scale} className="mt-5" />304            <p className="tnum mt-2 text-xs text-ink-3">305              {fmtInt(populated.length)} populated day{populated.length === 1 ? '' : 's'} in the window · sample sizes per day in the tooltip · a null median means no offer in that universe on that day.306            </p>307            <Methodology text={index.methodology ?? index.note} />308          </>309        )}310      </Section>311312      {/* ------------------------------------------------------------------------------------- distribution + frontier */}313      {index && (314        <Section eyebrow="Current offers" title="Distribution and the cheapest frontier">315          <div className="grid grid-cols-[minmax(0,1fr)] gap-x-8 gap-y-4 md:grid-cols-[minmax(0,1.3fr)_minmax(0,1fr)_minmax(0,1fr)]">316            <div>317              <p className="eyebrow">Current offers by output price</p>318              <DistBars d={index.distribution} className="mt-2" />319            </div>320            <CheapestStrip c={index.cheapest_frontier} label="Cheapest frontier output" />321            <CheapestStrip c={index.cheapest_frontier_1m_context} label="Cheapest frontier with ≥ 1M context" />322          </div>323          <Methodology text={index.frontier?.methodology} />324        </Section>325      )}326327      {/* ------------------------------------------------------------------------------------------------ movers */}328      <Section eyebrow="Movers" title="Recent price changes" lede="Provider offers whose published price moved, newest first, with the relative change." action={{ href: `${routes.changes()}?type=PRICE_CHANGED`, label: 'All pricing events' }}>329        {!index ? <Unavailable what="Price changes" /> : <PriceMovers movers={index.movers ?? []} providerHref={providerHref} />}330      </Section>331332      {/* ---------------------------------------------------------------------------------- listings / delistings */}333      <Section eyebrow="Listings · 30 days" title="New listings and delistings">334        <div className="grid grid-cols-[minmax(0,1fr)] gap-8 md:grid-cols-2">335          <div>336            <p className="flex items-baseline justify-between">337              <span className="text-sm font-medium text-ink">New listings</span>338              <span className="tnum text-sm text-ink-3">{fmtInt(asCount(newListings))} in 30 d</span>339            </p>340            <p className="mt-0.5 text-xs text-ink-3">PROVIDER_LISTED events that occurred in the last 30 days (a model × provider offer first opened).</p>341            {Array.isArray(newListings) && newListings.length ? (342              <ul className="mt-2 border-t border-rule">{newListings.slice(0, 8).map((e) => <ChangeRow key={e.id} e={e} dense live={false} showDate />)}</ul>343            ) : listed?.items.length ? (344              <ul className="mt-2 border-t border-rule">{listed.items.map((e) => <ChangeRow key={e.id} e={e} dense live={false} showDate />)}</ul>345            ) : (346              <p className="mt-2 text-sm text-ink-3">No listing event in the window.</p>347            )}348            <p className="mt-2 text-xs">349              <Link href={`${routes.changes()}?type=PROVIDER_LISTED`} className="link">All listing events →</Link>350            </p>351          </div>352          <div>353            <p className="flex items-baseline justify-between">354              <span className="text-sm font-medium text-ink">Delistings</span>355              <span className="tnum text-sm text-ink-3">{fmtInt(asCount(delistings))} in 30 d</span>356            </p>357            <p className="mt-0.5 text-xs text-ink-3">PROVIDER_DELISTED events that occurred in the last 30 days (an offer closed).</p>358            {Array.isArray(delistings) && delistings.length ? (359              <ul className="mt-2 border-t border-rule">{delistings.slice(0, 8).map((e) => <ChangeRow key={e.id} e={e} dense live={false} showDate />)}</ul>360            ) : delisted?.items.length ? (361              <ul className="mt-2 border-t border-rule">{delisted.items.map((e) => <ChangeRow key={e.id} e={e} dense live={false} showDate />)}</ul>362            ) : (363              <p className="mt-2 text-sm text-ink-3">No delisting recorded in the window — offers that disappear from a pricing page are closed (valid_to) and emit PROVIDER_DELISTED.</p>364            )}365          </div>366        </div>367      </Section>368369      {/* ------------------------------------------------------------------------------------------------ offers */}370      <Section eyebrow="Current offers" title={providerName ? `Every current offer from ${providerName}` : cur.model ? `Current offers for ${cur.model}` : 'Every current offer'} lede="One row per model × provider, as published. Expand a row to load its price history (sparklines). Sort with the column headers or the rail.">371        {priced.notFound ? (372          <EmptyState title={`No ${cur.model ? 'model' : cur.provider ? 'provider' : 'entity'} with slug “${cur.model ?? cur.provider ?? cur.org ?? cur.family}”`}>373            Slugs are the last part of an entity URL (<span className="mono">/models/&lt;slug&gt;</span>). <Link href={cur.model ? routes.search(cur.model, 'model') : routes.providers()} className="link">Search instead →</Link>374          </EmptyState>375        ) : !page ? (376          <Unavailable what="Current prices" reason={priced.detail ?? undefined} />377        ) : (378          <>379            {page.methodology && sort === 'cheapest_frontier' && <Methodology text={page.methodology} className="mb-3 mt-0" />}380            <DataTable scroll compact>381              <thead>382                <tr>383                  <SortTh active={sort === 'model'} href={href({ sort: 'model', offset: undefined })}>Model</SortTh>384                  <SortTh active={sort === 'provider'} href={href({ sort: 'provider', offset: undefined })}>Provider</SortTh>385                  <SortTh active={sort === 'input'} href={href({ sort: undefined, offset: undefined })} num>Input / 1M</SortTh>386                  <Th num>Cached in</Th>387                  <SortTh active={sort === 'output' || sort === 'cheapest_frontier'} href={href({ sort: 'output', offset: undefined })} num>Output / 1M</SortTh>388                  <Th num>Batch in / out</Th>389                  <Th num>Context</Th>390                  <SortTh active={sort === 'observed'} href={href({ sort: 'observed', offset: undefined })} dir="desc">Observed</SortTh>391                  <Th>Source</Th>392                  <Th className="w-24" aria-label="History" />393                </tr>394              </thead>395              <tbody>396                {rows.length === 0 && <EmptyRow cols={10}>{sort === 'cheapest_frontier' ? 'No frontier model has a current priced offer in the API response (see the methodology above).' : 'No current offers match these filters.'}</EmptyRow>}397                {rows.map((p) => {398                  const v = num(p.input_per_mtok);399                  const cheapest = v !== null && minByModel.get(p.model.slug) === v && (providersByModel.get(p.model.slug)?.size ?? 0) > 1;400                  return (401                    <ExpandableOfferRow key={p.id} model={p.model.slug} provider={p.provider.slug} colSpan={9}>402                      <Td primary>403                        <EntityLink e={p.model} />404                        {p.model.organization && <span className="ml-2 text-xs text-ink-3">{p.model.organization.name}</span>}405                        {p.provider_model_id && p.provider_model_id !== p.model.slug && <span className="mono block text-[11px] text-ink-3">{p.provider_model_id}</span>}406                      </Td>407                      <Td label="Provider" className="text-ink-2">408                        <Link href={href({ provider: p.provider.slug, offset: undefined })} className="hover:text-accent" title="Filter by this provider">409                          {p.provider.name}410                        </Link>411                        <EntityLink e={p.provider} className="ml-1.5 text-xs text-ink-3">↗</EntityLink>412                      </Td>413                      <Td num label="Input / 1M" className={cheapest ? 'tnum font-semibold text-accent-2' : 'tnum text-accent-2'}>414                        {fmtUsdPerM(p.input_per_mtok)}415                        {cheapest && <Chip tone="accent" className="ml-1.5 align-middle">cheapest</Chip>}416                      </Td>417                      <Td num label="Cached in" className="tnum text-ink-2">{fmtUsdPerM(p.cached_input_per_mtok)}</Td>418                      <Td num label="Output / 1M" className="tnum text-accent-2">{fmtUsdPerM(p.output_per_mtok)}</Td>419                      <Td num label="Batch" className="tnum text-ink-2">420                        {num(p.batch_input_per_mtok) === null && num(p.batch_output_per_mtok) === null ? <span className="text-ink-3">—</span> : `${fmtUsdPerM(p.batch_input_per_mtok)} / ${fmtUsdPerM(p.batch_output_per_mtok)}`}421                      </Td>422                      <Td num label="Context" className="tnum text-ink-2">{num(p.context_length) === null ? <span className="text-ink-3">—</span> : fmtTokens(p.context_length)}</Td>423                      <Td label="Observed" className="text-ink-2" title={p.observed_at}>{fmtAgo(p.observed_at)}</Td>424                      <Td label="Source"><SourceCell url={p.source_url} tier={p.tier} /></Td>425                    </ExpandableOfferRow>426                  );427                })}428              </tbody>429            </DataTable>430            <Pagination total={page.total} limit={LIMIT} offset={offset} makeHref={(o) => href({ offset: o || undefined })} className="mt-4" />431            <Note className="mt-3">432              “Cheapest” marks the lowest input price for a model among the rows on this page (models served by several providers). USD per 1M tokens as published; <Link href={routes.providers()} className="link">providers overview</Link> · <Link href={routes.calculator()} className="link">cost calculator</Link> · <Link href="/methodology" className="link">methodology</Link>.433            </Note>434          </>435        )}436      </Section>437    </TerminalLayout>438  );439}440