import Link from 'next/link'; import { ScrollX } from '@/components/models/scroll-x'; import { DataTable, Td, Th } from '@/components/ui/data-table'; import { KeyValue, type KVRow } from '@/components/ui/key-value'; import { SourceCell } from '@/components/ui/provenance'; import { Note } from '@/components/ui/section'; import { EmptyState, Unavailable } from '@/components/ui/unavailable'; import { cn } from '@/lib/cn'; import { fmtDate, fmtDateTime, fmtInt, fmtValue } from '@/lib/format'; import { PROSE_KEYS, propertyLabel, routes } from '@/lib/site'; import type { AsOfPayload, Claim, EntityDetail } from '@/lib/types'; import { AsOfPicker } from './asof-picker'; /* Canonical order for model properties (mirrors SpecTable in blocks.tsx; kept local to avoid touching that file). */ const MODEL_ORDER = ['family', 'version', 'release_date', 'status', 'openness', 'license', 'architecture', 'parameter_count', 'active_parameter_count', 'is_moe', 'context_length', 'max_output_tokens', 'knowledge_cutoff', 'training_data_cutoff', 'modalities', 'modalities_input', 'modalities_output', 'languages', 'tokenizer', 'api_model_id', 'api_alias', 'base_model', 'quantization', 'quant_format', 'file_size_gb', 'deprecation_date', 'retirement_date', 'retirement_tentative', 'hf_repo', 'pipeline_tag', 'official_url', 'model_card_url', 'paper_url', 'repository_url', 'metric.downloads', 'metric.likes']; const HIDDEN = new Set(['name', 'slug', 'id', 'entity_type']); const ORDER = new Map(MODEL_ORDER.map((k, i) => [k, i])); const byCanonical = (a: string, b: string) => (ORDER.get(a) ?? 999) - (ORDER.get(b) ?? 999) || a.localeCompare(b); const STATUS_CLS: Record = { current: 'text-positive bg-positive-soft', superseded: 'text-ink-3 bg-surface-2', conflicting: 'text-danger bg-danger-soft', retracted: 'text-warning bg-warning-soft', }; function ClaimStatus({ status }: { status: string }) { return {status}; } /* ------------------------------------------------------------------------------------------------------ as of */ export function AsOfBlock({ d, asof, payload }: { d: EntityDetail; asof: string; payload: AsOfPayload | null }) { const back = `${routes.entity(d)}?tab=history`; if (!payload) { return (

Back to today →

); } const attrs = payload.attributes ?? {}; const keys = Object.keys(attrs).filter((k) => !HIDDEN.has(k) && !PROSE_KEYS.has(k) && attrs[k] !== null && attrs[k] !== undefined && attrs[k] !== '' && !(Array.isArray(attrs[k]) && (attrs[k] as unknown[]).length === 0)); keys.sort(byCanonical); const rows: KVRow[] = keys.map((k) => ({ key: k, raw: attrs[k] })); return (

Viewing AI Atlas as of {fmtDate(payload.date)} — attributes exactly as the atlas knew them on that day; later corrections are not shown.

Back to today →
{!payload.existed ? ( First seen {fmtDate(payload.first_seen_at)}. Nothing is inferred backwards: no attribute is shown for dates before the first observation. ) : (

Attributes as of {fmtDate(payload.date)} {fmtInt(payload.claims?.length ?? 0)} claims in force

)}
); } /* ------------------------------------------------------------------------------------------------------ claim history */ export function ClaimHistory({ d, claims, property }: { d: EntityDetail; claims: Claim[] | null; property?: string }) { const base = `${routes.entity(d)}?tab=history`; if (!claims) return ; if (!claims.length) return ( {property ? Show all properties → : 'Claims appear when a source states a fact; every later change is kept as a new claim.'} ); const groups = new Map(); for (const c of claims) { const arr = groups.get(c.property); if (arr) arr.push(c); else groups.set(c.property, [c]); } const keys = [...groups.keys()].sort(byCanonical); const conflicting = claims.filter((c) => c.status === 'conflicting').length; return (
{fmtInt(claims.length)} claims · {fmtInt(keys.length)} properties {conflicting > 0 && {fmtInt(conflicting)} conflicting} {property && ( Show all properties )}
{keys.map((k) => { const rows = groups.get(k) ?? []; return (

{propertyLabel(k)} {k} {fmtInt(rows.length)} {rows.some((c) => c.status === 'conflicting') && conflicting claims}

Value Valid from → to Status Source Confidence Extractor {rows.map((c) => ( {fmtValue(c.value, c.property)} {c.unit && !/parameter_count|context_length|max_output_tokens|memory_gb|file_size_gb/.test(c.property) && {c.unit}} → {c.valid_to ? : current} {c.confidence} {c.extractor} ))}
); })} Claims are temporal and append-only: a new observation closes the previous claim (valid_to) instead of overwriting it. Conflicting claims from different sources are kept side by side and flagged — never averaged. Methodology →
); } /* ------------------------------------------------------------------------------------------------------ panel */ export function HistoryPanel({ d, asof, asofPayload, claims, property }: { d: EntityDetail; asof?: string; asofPayload: AsOfPayload | null; claims: Claim[] | null; property?: string }) { return (

As of

Rewind the record: see this entity's attributes exactly as AI Atlas knew them on a given day.

{asof &&
}

Claim history{property ? <> · {propertyLabel(property)} : null}

); }