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%
8.4 KB · 207 lines typescript
Raw Blame History
1/**2 * Formatting helpers. API timestamps are UTC ISO strings; dates render in UTC. All helpers accept `Num`3 * (Postgres aggregates can arrive as strings) and return the em-dash for missing values — never a fake number.4 */5import type { Num } from './types';67const nf0 = new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 });8const nf1 = new Intl.NumberFormat('en-US', { maximumFractionDigits: 1 });9const nf2 = new Intl.NumberFormat('en-US', { maximumFractionDigits: 2 });10const nfCompact = new Intl.NumberFormat('en-US', { notation: 'compact', maximumFractionDigits: 1 });1112export const DASH = '—';1314/** Coerce `Num`/unknown to a finite number or null. */15export function num(v: unknown): number | null {16  if (v === null || v === undefined || v === '') return null;17  const n = typeof v === 'number' ? v : Number(v);18  return Number.isFinite(n) ? n : null;19}2021export function fmtInt(v: Num | undefined | unknown): string {22  const n = num(v);23  return n === null ? DASH : nf0.format(n);24}25export function fmt1(v: Num | undefined | unknown): string {26  const n = num(v);27  return n === null ? DASH : nf1.format(n);28}29export function fmt2(v: Num | undefined | unknown): string {30  const n = num(v);31  return n === null ? DASH : nf2.format(n);32}33export function fmtCompact(v: Num | undefined | unknown): string {34  const n = num(v);35  return n === null ? DASH : nfCompact.format(n);36}3738/** Parameter counts: 7e9 → "7B", 1.8e12 → "1.8T", 350e6 → "350M". */39export function fmtParams(v: Num | undefined | unknown): string {40  const n = num(v);41  if (n === null) return DASH;42  if (n >= 1e12) return `${trim(n / 1e12)}T`;43  if (n >= 1e9) return `${trim(n / 1e9)}B`;44  if (n >= 1e6) return `${trim(n / 1e6)}M`;45  if (n >= 1e3) return `${trim(n / 1e3)}K`;46  return nf0.format(n);47}48/** Token windows: 128000 → "128K", 1000000 → "1M", 200000 → "200K". */49export function fmtTokens(v: Num | undefined | unknown): string {50  const n = num(v);51  if (n === null) return DASH;52  if (n >= 1e6) return `${trim(n / 1e6)}M`;53  if (n >= 1e3) return `${trim(n / 1e3)}K`;54  return nf0.format(n);55}56function trim(x: number): string {57  const r = Math.round(x * 100) / 100;58  return r % 1 === 0 ? String(r) : String(Number(r.toFixed(r < 10 ? 2 : 1)));59}6061/** USD per 1M tokens: 3 → "$3.00", 0.075 → "$0.075", 15 → "$15.00". */62export function fmtUsdPerM(v: Num | undefined | unknown, withUnit = false): string {63  const n = num(v);64  if (n === null) return DASH;65  let s: string;66  if (n === 0) s = '$0';67  else if (n < 0.01) s = `$${n.toFixed(4).replace(/0+$/, '')}`;68  else if (n < 1) s = `$${n.toFixed(3).replace(/0$/, '')}`;69  else s = `$${nf2.format(n)}`;70  return withUnit ? `${s} / 1M` : s;71}72export function fmtUsd(v: Num | undefined | unknown): string {73  const n = num(v);74  return n === null ? DASH : `$${nf0.format(n)}`;75}76export function fmtPct(v: Num | undefined | unknown, digits = 1): string {77  const n = num(v);78  return n === null ? DASH : `${n.toFixed(digits)}%`;79}80export function fmtGb(v: Num | undefined | unknown, digits = 0): string {81  const n = num(v);82  return n === null ? DASH : `${digits ? n.toFixed(digits) : nf0.format(n)} GB`;83}84export function fmtBytes(v: Num | undefined | unknown): string {85  const n = num(v);86  if (n === null) return DASH;87  if (n >= 1e12) return `${(n / 1e12).toFixed(2)} TB`;88  if (n >= 1e9) return `${(n / 1e9).toFixed(1)} GB`;89  if (n >= 1e6) return `${(n / 1e6).toFixed(0)} MB`;90  if (n >= 1e3) return `${(n / 1e3).toFixed(0)} kB`;91  return `${n} B`;92}93export function fmtScore(v: Num | undefined | unknown): string {94  const n = num(v);95  if (n === null) return DASH;96  return Number.isInteger(n) ? nf0.format(n) : nf2.format(n);97}9899/** ISO date (YYYY-MM-DD, YYYY-MM or full timestamp) → "11 Sept 2026" / "Sept 2026" / "2026" (UTC). */100export function fmtDate(v: string | null | undefined): string {101  if (!v) return DASH;102  if (/^\d{4}$/.test(v)) return v;103  if (/^\d{4}-\d{2}$/.test(v)) return fmtMonth(v);104  const d = new Date(v.length === 10 ? `${v}T00:00:00Z` : v);105  if (Number.isNaN(d.getTime())) return v;106  return d.toLocaleDateString('en-GB', { year: 'numeric', month: 'short', day: 'numeric', timeZone: 'UTC' });107}108export function fmtMonth(v: string | null | undefined): string {109  if (!v) return DASH;110  const m = /^(\d{4})-(\d{2})/.exec(v);111  if (!m) return fmtDate(v);112  const d = new Date(Date.UTC(Number(m[1]), Number(m[2]) - 1, 1));113  return d.toLocaleDateString('en-GB', { year: 'numeric', month: 'short', timeZone: 'UTC' });114}115export function fmtDateTime(v: string | null | undefined): string {116  if (!v) return DASH;117  const d = new Date(v);118  if (Number.isNaN(d.getTime())) return DASH;119  return `${d.toLocaleDateString('en-GB', { year: 'numeric', month: 'short', day: 'numeric', timeZone: 'UTC' })} ${d.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', timeZone: 'UTC' })} UTC`;120}121export function fmtAgo(v: string | null | undefined, now: number = Date.now()): string {122  if (!v) return 'unavailable';123  const t = new Date(v).getTime();124  if (Number.isNaN(t)) return 'unavailable';125  const s = Math.max(0, Math.round((now - t) / 1000));126  if (s < 60) return 'just now';127  const m = Math.round(s / 60);128  if (m < 60) return `${m} min ago`;129  const h = Math.round(m / 60);130  if (h < 48) return `${h} h ago`;131  const d = Math.round(h / 24);132  if (d < 60) return `${d} d ago`;133  return fmtDate(v);134}135export function fmtYear(v: string | null | undefined): string {136  return v && /^\d{4}/.test(v) ? v.slice(0, 4) : DASH;137}138export function fmtDuration(seconds: Num | undefined | unknown): string {139  const n = num(seconds);140  if (n === null) return DASH;141  if (n < 60) return `${n}s`;142  if (n < 3600) return `${Math.round(n / 60)} min`;143  if (n < 86400) return `${Math.round(n / 3600)} h`;144  return `${Math.round(n / 86400)} d`;145}146147/** Signed delta: +3.2% / −1.8% (relative change between two values). Null when either side is missing or the base is 0. */148export function fmtDeltaPct(from: Num | undefined | unknown, to: Num | undefined | unknown, digits = 1): string | null {149  const a = num(from);150  const b = num(to);151  if (a === null || b === null || a === 0) return null;152  const d = ((b - a) / Math.abs(a)) * 100;153  const sign = d > 0 ? '+' : d < 0 ? '−' : '';154  return `${sign}${Math.abs(d).toFixed(digits)}%`;155}156/** Signed integer: +12 / −3 / 0. */157export function fmtSigned(v: Num | undefined | unknown): string {158  const n = num(v);159  if (n === null) return DASH;160  return n > 0 ? `+${nf0.format(n)}` : n < 0 ? `−${nf0.format(Math.abs(n))}` : '0';161}162/** Host of a URL without `www.` — for compact source labels. */163export function hostOf(url: string | null | undefined): string | null {164  if (!url) return null;165  try {166    return new URL(url).hostname.replace(/^www\./, '');167  } catch {168    return null;169  }170}171172export function titleCase(s: string | null | undefined): string {173  if (!s) return DASH;174  return s.replace(/[_-]+/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());175}176/** Event type / predicate constant → words: NEW_MODEL → "New model", fine_tuned_from → "fine-tuned from". */177export function humanize(s: string | null | undefined): string {178  if (!s) return DASH;179  const w = s.toLowerCase().replace(/_/g, ' ');180  return w.charAt(0).toUpperCase() + w.slice(1);181}182export function plural(n: number, one: string, many = `${one}s`): string {183  return n === 1 ? one : many;184}185186/** Render any attribute value for a spec table (never JSON-dumps a primitive). */187export function fmtValue(v: unknown, key?: string): string {188  if (v === null || v === undefined || v === '') return DASH;189  if (typeof v === 'boolean') return v ? 'Yes' : 'No';190  if (Array.isArray(v)) return v.length ? v.map((x) => (typeof x === 'object' && x ? JSON.stringify(x) : String(x))).join(', ') : DASH;191  if (typeof v === 'number') {192    if (key && /parameter_count/.test(key)) return fmtParams(v);193    if (key && /(context_length|max_output_tokens)/.test(key)) return `${fmtTokens(v)} tokens`;194    if (key && /memory_gb|file_size_gb/.test(key)) return fmtGb(v, v % 1 ? 1 : 0);195    if (key && /bandwidth_gbs/.test(key)) return `${fmtInt(v)} GB/s`;196    if (key && /tdp_watts/.test(key)) return `${fmtInt(v)} W`;197    if (key && /tflops/.test(key)) return `${fmt1(v)} TFLOPS`;198    if (key && /price_usd/.test(key)) return fmtUsd(v);199    return Number.isInteger(v) ? nf0.format(v) : nf2.format(v);200  }201  if (typeof v === 'string') {202    if (key && /(_date|_at|cutoff)$/.test(key) && /^\d{4}(-\d{2})?(-\d{2})?/.test(v)) return fmtDate(v);203    return v;204  }205  return JSON.stringify(v);206}207