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%
16.3 KB · 296 lines tsx
Raw Blame History
1import { Check, Minus, X } from 'lucide-react';2import Link from 'next/link';3import type { ReactNode } from 'react';4import { Estimated } from '@/components/ui/badges';5import { Hint } from '@/components/ui/hint';6import { Note } from '@/components/ui/section';7import { cn } from '@/lib/cn';8import { fmtGb, fmtInt, fmtUsdPerM, num } from '@/lib/format';9import type { Fit, ModelLicence, Num, PriceDistribution, PriceDistributionStats } from '@/lib/types';1011/*12  Small server-safe building blocks shared by the intelligence surfaces (frontier · prices · providers · open · run-locally …).13  Every number is passed in from the API; nothing here computes a score.14*/1516/** Methodology footnote under a section: the API's own `methodology` / `note` string, never paraphrased into a claim. */17export function Methodology({ text, children, className }: { text?: string | null; children?: ReactNode; className?: string }) {18  if (!text && !children) return null;19  return (20    <Note className={cn('mt-3 max-w-4xl', className)}>21      <span className="eyebrow mr-1.5 text-[10px]">Method</span>22      {text}23      {children}{' '}24      <Link href="/methodology" className="text-ink-3 underline decoration-dotted hover:text-ink">25        /methodology26      </Link>27    </Note>28  );29}3031/** Tiny min · p25 · median · p75 · max range bar on a log₁₀ axis shared by the whole column (`domain`). Amber = money. */32export function RangeBar({ d, domain, format = (v) => fmtUsdPerM(v), className, label }: { d: PriceDistributionStats | null | undefined; domain: [number, number]; format?: (v: number) => string; className?: string; label?: string }) {33  const min = num(d?.min);34  const p25 = num(d?.p25);35  const med = num(d?.median);36  const p75 = num(d?.p75);37  const max = num(d?.max);38  if (min === null || max === null || med === null) return <span className="text-xs text-ink-3">—</span>;39  const [lo, hi] = domain;40  const L = (v: number) => Math.log10(Math.max(v, lo));41  const pct = (v: number) => (hi === lo ? 50 : ((L(v) - L(lo)) / (L(hi) - L(lo))) * 100);42  const title = `${label ?? 'Distribution'}: min ${format(min)} · p25 ${p25 === null ? '—' : format(p25)} · median ${format(med)} · p75 ${p75 === null ? '—' : format(p75)} · max ${format(max)}${num(d?.n) !== null ? ` · n=${fmtInt(d?.n)}` : ''}`;43  return (44    <span className={cn('inline-flex items-center gap-2', className)} title={title} aria-label={title}>45      <span className="relative block h-3 w-24 shrink-0" aria-hidden>46        <span className="absolute inset-y-[5px] left-0 right-0 rounded-sm bg-surface-3" />47        <span className="absolute inset-y-[5px] rounded-sm bg-accent-2/60" style={{ left: `${pct(min)}%`, width: `${Math.max(1, pct(max) - pct(min))}%` }} />48        {p25 !== null && p75 !== null && <span className="absolute inset-y-[3px] rounded-sm bg-accent-2" style={{ left: `${pct(p25)}%`, width: `${Math.max(1.5, pct(p75) - pct(p25))}%` }} />}49        <span className="absolute inset-y-0 w-[2px] bg-ink" style={{ left: `calc(${pct(med)}% - 1px)` }} />50      </span>51      <span className="tnum text-xs text-ink-2">52        {format(med)} <span className="text-ink-3">med</span>53      </span>54    </span>55  );56}5758/** Log-domain helper for a column of distributions. */59export function distDomain(items: (PriceDistributionStats | null | undefined)[]): [number, number] {60  const mins = items.map((d) => num(d?.min)).filter((v): v is number => v !== null && v > 0);61  const maxs = items.map((d) => num(d?.max)).filter((v): v is number => v !== null && v > 0);62  if (!mins.length || !maxs.length) return [0.01, 100];63  return [Math.min(...mins), Math.max(...maxs)];64}6566/** Histogram of current offers by price bucket (from `/prices/index.distribution`). */67export function DistBars({ d, className }: { d: PriceDistribution | null | undefined; className?: string }) {68  const buckets = d?.buckets ?? [];69  const vals = buckets.map((b) => num(b.offers) ?? 0);70  const max = Math.max(1, ...vals);71  if (!buckets.length) return <p className={cn('text-xs text-ink-3', className)}>No distribution returned.</p>;72  return (73    <div className={className}>74      <ol className="flex h-28 items-end gap-1" role="img" aria-label={`Current offers by ${d?.metric ?? 'price'} bucket`}>75        {buckets.map((b, i) => {76          const v = vals[i] ?? 0;77          return (78            <li key={b.label} className="group relative flex min-w-0 flex-1 flex-col items-center justify-end" title={`${b.label}: ${fmtInt(v)} offers`}>79              <span className="tnum mb-0.5 text-[10px] text-ink-3">{v ? fmtInt(v) : ''}</span>80              <span className="block w-full rounded-t-[2px] bg-accent-2/80 group-hover:bg-accent-2" style={{ height: `${Math.max(2, (v / max) * 80)}px` }} />81            </li>82          );83        })}84      </ol>85      <ol className="mt-1 flex gap-1 border-t border-rule pt-1" aria-hidden>86        {buckets.map((b) => (87          <li key={b.label} className="tnum min-w-0 flex-1 truncate text-center text-[9px] text-ink-3 sm:text-[10px]">88            {b.label}89          </li>90        ))}91      </ol>92      <p className="tnum mt-1 text-xs text-ink-3">93        {fmtInt(d?.offers)} current offers · {d?.unit ?? 'USD per 1M tokens'} · {d?.metric?.replace(/_per_mtok$/, '').replace(/_/g, ' ')}94      </p>95    </div>96  );97}9899/** Estimated fit result: ✓/✗ + estimated memory + headroom, always next to an `Estimated` label. `compact` for table cells. */100export function FitCell({ fit, compact = false, className }: { fit: Partial<Fit> | null | undefined; compact?: boolean; className?: string }) {101  if (!fit || fit.fits === null || fit.fits === undefined) return <span className={cn('text-xs text-ink-3', className)} aria-label="no estimate">—</span>;102  const mem = num(fit.estimated_memory_gb);103  const head = num(fit.headroom_gb);104  return (105    <span className={cn('tnum inline-flex flex-wrap items-center gap-x-1.5 gap-y-0.5 text-xs', className)}>106      <span className={fit.fits ? 'font-medium text-positive' : 'text-danger'}>{fit.fits ? '✓ fits' : '✗ too large'}</span>107      {mem !== null && <span className="text-ink-2">{fmtGb(mem, 1)}</span>}108      {!compact && head !== null && <span className="text-ink-3">{head >= 0 ? '+' : '−'}{fmtGb(Math.abs(head), 1)} headroom</span>}109      {fit.quantization && <span className="mono text-[10px] text-ink-3">{fit.quantization}</span>}110    </span>111  );112}113114/** Fit breakdown (weights est./observed · KV cache + method · overhead · reserve) as a compact inline list. */115export function FitBreakdownList({ fit, className }: { fit: Fit; className?: string }) {116  const b = fit.breakdown;117  if (!b) return fit.note ? <p className={cn('text-xs text-ink-3', className)}>{fit.note}</p> : null;118  return (119    <ul className={cn('tnum flex flex-wrap gap-x-3 gap-y-0.5 text-[11px] text-ink-3', className)}>120      <li>121        weights <span className="text-ink-2">{fmtGb(b.weights_gb, 1)}</span> <SourceTag source={b.weights_source} />122      </li>123      <li>124        KV cache <span className="text-ink-2">{fmtGb(b.kv_cache_gb, 2)}</span> <span className="mono">({b.kv_cache_method})</span>125      </li>126      <li>127        overhead <span className="text-ink-2">{fmtGb(b.overhead_gb, 1)}</span>128      </li>129      <li>130        reserved <span className="text-ink-2">{fmtGb(b.reserved_gb, 0)}</span>131      </li>132      <li>133        context <span className="text-ink-2">{fmtInt(b.context)}</span> × batch {fmtInt(b.batch)}134      </li>135    </ul>136  );137}138139/** "observed" (green, file size read from the artifact) vs "estimated" (dashed warning). */140export function SourceTag({ source }: { source: string | null | undefined }) {141  if (source === 'observed') return <span className="rounded-[3px] bg-positive-soft px-1 text-[10px] font-medium uppercase tracking-wide text-positive">observed</span>;142  return <span className="rounded-[3px] border border-dashed border-warning/60 px-1 text-[10px] font-medium uppercase tracking-wide text-warning">estimated</span>;143}144145/** Banner above any table of estimates: label + the API assumptions (never our own wording). */146export function EstimateBanner({ assumptions, note, counts, className }: { assumptions?: string[]; note?: string | null; counts?: { fits?: Num; evaluated?: Num } | null; className?: string }) {147  return (148    <div className={cn('border-y border-rule py-3', className)} data-estimate-banner>149      <p className="flex flex-wrap items-center gap-x-3 gap-y-1 text-sm">150        <Estimated />151        <span className="font-semibold text-ink">All fit figures are estimates, not measurements.</span>152        {counts && num(counts.fits) !== null && num(counts.evaluated) !== null && (153          <span className="tnum text-ink-3">154            <span className="font-medium text-positive">{fmtInt(counts.fits)}</span> of {fmtInt(counts.evaluated)} evaluated models fit155          </span>156        )}157      </p>158      {note && <p className="mt-1.5 text-xs leading-relaxed text-ink-2">{note}</p>}159      {assumptions && assumptions.length > 0 && (160        <details className="mt-1.5 text-xs text-ink-3">161          <summary className="cursor-pointer select-none hover:text-ink">Assumptions ({assumptions.length})</summary>162          <ul className="mt-1 list-disc space-y-0.5 pl-5 leading-relaxed">163            {assumptions.map((a) => (164              <li key={a}>{a}</li>165            ))}166          </ul>167        </details>168      )}169    </div>170  );171}172173/** Licence permission glyphs: commercial · redistribution · derivatives · hosting (✓ allowed · ✗ restricted · – unknown). */174export function LicencePerms({ l, className, withLabel = false }: { l: ModelLicence | null | undefined; className?: string; withLabel?: boolean }) {175  if (!l || l.key === null) {176    return (177      <span className={cn('inline-flex flex-wrap items-center gap-1 text-xs text-ink-3', className)} title={l && 'note' in l && l.note ? l.note : 'Licence not classified'}>178        {l?.raw ? <span className="max-w-[9rem] truncate text-ink-2">{l.raw}</span> : <span>—</span>}179        <span className="rounded-[3px] bg-surface-2 px-1 text-[10px] uppercase tracking-wide">unclassified</span>180      </span>181    );182  }183  const items: [string, boolean | null | undefined, boolean][] = [184    ['Commercial use', l.commercial_use, false],185    ['Redistribution', l.redistribution, false],186    ['Derivatives', l.derivatives, false],187    ['Hosting', l.hosting_restrictions === null || l.hosting_restrictions === undefined ? null : !l.hosting_restrictions, false],188  ];189  return (190    <span className={cn('inline-flex flex-wrap items-center gap-x-1.5 gap-y-0.5 text-xs', className)}>191      {withLabel && <span className="mr-1 truncate text-ink">{l.label ?? l.key}</span>}192      {!withLabel && <span className="mono mr-0.5 text-[11px] text-ink-2">{l.key}</span>}193      {items.map(([label, v]) => (194        <span key={label} className={cn('inline-flex size-4 items-center justify-center rounded-[3px]', v === true ? 'bg-positive-soft text-positive' : v === false ? 'bg-danger-soft text-danger' : 'bg-surface-2 text-ink-3')} title={`${label}: ${v === true ? 'allowed' : v === false ? 'restricted' : 'unknown'}`} aria-label={`${label}: ${v === true ? 'allowed' : v === false ? 'restricted' : 'unknown'}`}>195          {v === true ? <Check className="size-3" aria-hidden /> : v === false ? <X className="size-3" aria-hidden /> : <Minus className="size-3" aria-hidden />}196        </span>197      ))}198    </span>199  );200}201export function LicenceLegend({ className }: { className?: string }) {202  return (203    <span className={cn('flex flex-wrap items-center gap-x-3 gap-y-1 text-[11px] text-ink-3', className)}>204      <span>Permissions, in order:</span>205      <span>commercial use</span>206      <span>· redistribution</span>207      <span>· derivatives</span>208      <span>· hosting</span>209      <span className="inline-flex items-center gap-1">210        <span className="inline-flex size-3.5 items-center justify-center rounded-[2px] bg-positive-soft text-positive"><Check className="size-2.5" aria-hidden /></span> allowed211      </span>212      <span className="inline-flex items-center gap-1">213        <span className="inline-flex size-3.5 items-center justify-center rounded-[2px] bg-danger-soft text-danger"><X className="size-2.5" aria-hidden /></span> restricted214      </span>215      <span className="inline-flex items-center gap-1">216        <span className="inline-flex size-3.5 items-center justify-center rounded-[2px] bg-surface-2 text-ink-3"><Minus className="size-2.5" aria-hidden /></span> unknown217      </span>218    </span>219  );220}221222/** Trust level chip (leaderboard rows). */223const TRUST_TONE: Record<string, string> = {224  'official-benchmark': 'bg-positive-soft text-positive',225  'peer-reviewed': 'bg-positive-soft text-positive',226  'independent-evaluator': 'bg-accent-soft text-accent',227  'official-model-card': 'bg-warning-soft text-warning',228  community: 'bg-surface-2 text-ink-2',229  unverified: 'bg-danger-soft text-danger',230};231export function TrustChip({ level, label, className }: { level: string | null | undefined; label?: string | null; className?: string }) {232  if (!level) return <span className={cn('text-xs text-ink-3', className)}>—</span>;233  return (234    <span className={cn('inline-flex items-center whitespace-nowrap rounded-[3px] px-1.5 py-[1px] text-[11px] font-medium leading-4', TRUST_TONE[level] ?? 'bg-surface-2 text-ink-2', className)} title={label ?? level}>235      {level.replace(/-/g, ' ')}236    </span>237  );238}239240/** Rank chips "benchmark #n" (observed dimensions, never summed). */241export function RankChips({ ranks, max = 4, className }: { ranks: Record<string, number> | { benchmark: string; rank: number }[] | undefined | null; max?: number; className?: string }) {242  const arr = !ranks ? [] : Array.isArray(ranks) ? ranks : Object.entries(ranks).map(([benchmark, rank]) => ({ benchmark, rank }));243  const sorted = arr.slice().sort((a, b) => a.rank - b.rank);244  if (!sorted.length) return <span className={cn('text-xs text-ink-3', className)}>—</span>;245  return (246    <span className={cn('inline-flex flex-wrap gap-1', className)}>247      {sorted.slice(0, max).map((r) => (248        <Link key={r.benchmark} href={`/benchmarks/${encodeURIComponent(r.benchmark)}`} className="tnum inline-flex items-center gap-1 rounded-[3px] bg-surface-2 px-1.5 py-[1px] text-[11px] text-ink-2 hover:text-accent" title={`Rank ${r.rank} on ${r.benchmark} (primary comparability group)`}>249          <span className="truncate max-w-[7rem]">{r.benchmark}</span>250          <span className="font-medium text-ink">#{r.rank}</span>251        </Link>252      ))}253      {sorted.length > max && <span className="text-[11px] text-ink-3">+{sorted.length - max}</span>}254    </span>255  );256}257258/** Column header with a sort link (keeps the rest of the URL). */259export function SortTh({ active, href, children, num: n, dir = 'asc' }: { active: boolean; href: string; children: ReactNode; num?: boolean; dir?: 'asc' | 'desc' }) {260  return (261    <th scope="col" className={cn(n && 'num')} aria-sort={active ? (dir === 'asc' ? 'ascending' : 'descending') : undefined}>262      <Link href={href} className={active ? 'text-ink' : 'hover:text-ink'}>263        {children}264        {active && <span aria-hidden> {dir === 'asc' ? '↑' : '↓'}</span>}265      </Link>266    </th>267  );268}269270/** Label + definition tooltip pair used in rails and strips. */271export function Defined({ label, definition }: { label: ReactNode; definition?: string | null }) {272  return (273    <span className="inline-flex items-center gap-0.5">274      {label}275      {definition && <Hint text={definition} />}276    </span>277  );278}279280/** Shared form control classes for the GET forms in rails. */281export const CTRL = 'h-11 lg:h-10 w-full border border-rule bg-surface px-2.5 text-sm text-ink focus:border-accent focus:outline-none';282export const CTRL_LG = 'h-11 w-full border border-rule bg-surface px-2.5 text-[15px] text-ink focus:border-accent focus:outline-none';283export const BTN_PRIMARY = 'inline-flex h-11 items-center justify-center gap-1.5 bg-ink px-4 text-sm font-medium text-canvas hover:opacity-90';284export const BTN_GHOST = 'inline-flex h-11 lg:h-10 items-center justify-center border border-rule px-3 text-sm text-ink-2 hover:border-rule-strong hover:text-ink';285286/** Field wrapper for rail forms. */287export function Field({ label, children, hint, className }: { label: string; children: ReactNode; hint?: ReactNode; className?: string }) {288  return (289    <label className={cn('block min-w-0', className)}>290      <span className="eyebrow block pb-1">{label}</span>291      {children}292      {hint && <span className="mt-0.5 block text-[11px] text-ink-3">{hint}</span>}293    </label>294  );295}296