SPB Git

spb/ai-atlas

Public
17.9 KB · 477 lines typescript
1/** Site constants, routes per entity type, labels and colours shared by pages and components. */23export const SITE_NAME = 'AI Atlas';4export const SITE_URL = (process.env.NEXT_PUBLIC_SITE_URL ?? 'https://www.ai-atlas.co').replace(/\/$/, '');5export const TAGLINE = 'Explore the entire AI ecosystem';6export const DESCRIPTION =7  'AI Atlas is a continuously updated, source-attributed map of the global AI ecosystem: models, companies, research, providers and pricing, benchmarks, hardware, frameworks, datasets and tools — with full provenance and history.';8export const CONTACT_EMAIL = 'contact@spboucher.ai';9export const PUBLIC_API_BASE = 'https://www.ai-atlas.co/api/v1';10export const BOT_UA = 'AIAtlasBot';1112/** Entity type → URL path segment (plural). Types not listed fall back to /explore/<type>/<slug> → generic entity page. */13export const TYPE_PATH: Record<string, string> = {14  model: 'models',15  company: 'companies',16  organization: 'companies',17  lab: 'companies',18  university: 'companies',19  paper: 'papers',20  provider: 'providers',21  benchmark: 'benchmarks',22  hardware: 'hardware',23  framework: 'frameworks',24  library: 'frameworks',25  runtime: 'frameworks',26  dataset: 'datasets',27  tool: 'tools',28  agent: 'tools',29  mcp_server: 'tools',30  product: 'tools',31  repository: 'repositories',32};33/** URL path segment → API mount + canonical entity types accepted there. */34export const PATH_TYPES: Record<string, { label: string; singular: string; types: string[]; api: string }> = {35  models: { label: 'Models', singular: 'Model', types: ['model', 'quantization'], api: 'models' },36  companies: { label: 'Companies', singular: 'Company', types: ['company', 'organization', 'lab', 'university'], api: 'companies' },37  papers: { label: 'Research', singular: 'Paper', types: ['paper'], api: 'papers' },38  providers: { label: 'Providers', singular: 'Provider', types: ['provider'], api: 'providers' },39  benchmarks: { label: 'Benchmarks', singular: 'Benchmark', types: ['benchmark'], api: 'benchmarks' },40  hardware: { label: 'Hardware', singular: 'Hardware', types: ['hardware'], api: 'hardware' },41  frameworks: { label: 'Frameworks', singular: 'Framework', types: ['framework', 'library', 'runtime'], api: 'frameworks' },42  datasets: { label: 'Datasets', singular: 'Dataset', types: ['dataset'], api: 'datasets' },43  tools: { label: 'Tools', singular: 'Tool', types: ['tool', 'agent', 'mcp_server', 'product'], api: 'tools' },44  repositories: { label: 'Repositories', singular: 'Repository', types: ['repository'], api: 'entities' },45};4647export const TYPE_LABELS: Record<string, string> = {48  model: 'Model',49  company: 'Company',50  organization: 'Organization',51  lab: 'Lab',52  university: 'University',53  researcher: 'Researcher',54  paper: 'Paper',55  dataset: 'Dataset',56  benchmark: 'Benchmark',57  provider: 'Provider',58  framework: 'Framework',59  library: 'Library',60  runtime: 'Runtime',61  repository: 'Repository',62  tool: 'Tool',63  agent: 'Agent',64  mcp_server: 'MCP server',65  product: 'Product',66  hardware: 'Hardware',67  quantization: 'Quantization',68  license: 'License',69  regulation: 'Regulation',70  incident: 'Incident',71  release: 'Release',72  conference: 'Conference',73  country: 'Country',74  robot: 'Robot',75  data_center: 'Data center',76  standard: 'Standard',77  funding_round: 'Funding round',78  acquisition: 'Acquisition',79  course: 'Course',80  job: 'Job',81};82export const TYPE_LABELS_PLURAL: Record<string, string> = {83  model: 'Models',84  company: 'Companies',85  organization: 'Organizations',86  lab: 'Labs',87  university: 'Universities',88  researcher: 'Researchers',89  paper: 'Papers',90  dataset: 'Datasets',91  benchmark: 'Benchmarks',92  provider: 'Providers',93  framework: 'Frameworks',94  library: 'Libraries',95  runtime: 'Runtimes',96  repository: 'Repositories',97  tool: 'Tools',98  agent: 'Agents',99  mcp_server: 'MCP servers',100  product: 'Products',101  hardware: 'Hardware',102  quantization: 'Quantizations',103  license: 'Licenses',104  regulation: 'Regulations',105  incident: 'Incidents',106  release: 'Releases',107  conference: 'Conferences',108  country: 'Countries',109  robot: 'Robots',110  data_center: 'Data centers',111  standard: 'Standards',112  funding_round: 'Funding rounds',113  acquisition: 'Acquisitions',114  course: 'Courses',115  job: 'Jobs',116};117export function typeLabel(t: string, pluralForm = false): string {118  return (pluralForm ? TYPE_LABELS_PLURAL[t] : TYPE_LABELS[t]) ?? t.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());119}120121/** Badge colour family per entity type (CSS variables `--type-<key>` in globals.css). */122export const TYPE_COLOR_KEY: Record<string, string> = {123  model: 'model',124  quantization: 'model',125  company: 'company',126  organization: 'company',127  lab: 'company',128  university: 'company',129  researcher: 'company',130  paper: 'paper',131  provider: 'provider',132  benchmark: 'benchmark',133  hardware: 'hardware',134  robot: 'hardware',135  data_center: 'hardware',136  framework: 'framework',137  library: 'framework',138  runtime: 'framework',139  repository: 'framework',140  dataset: 'dataset',141  tool: 'tool',142  agent: 'tool',143  mcp_server: 'tool',144  product: 'tool',145};146147export const routes = {148  home: () => '/',149  search: (q: string, type?: string) => `/search?q=${encodeURIComponent(q)}${type ? `&type=${encodeURIComponent(type)}` : ''}`,150  models: () => '/models',151  companies: () => '/companies',152  papers: () => '/papers',153  providers: () => '/providers',154  benchmarks: () => '/benchmarks',155  hardware: () => '/hardware',156  frameworks: () => '/frameworks',157  datasets: () => '/datasets',158  tools: () => '/tools',159  changes: () => '/changes',160  changesDay: (date: string) => `/changes/${date}`,161  timeline: (q?: { entity?: string; year?: string; category?: string }) => {162    const p = new URLSearchParams();163    if (q?.entity) p.set('entity', q.entity);164    if (q?.year) p.set('year', q.year);165    if (q?.category) p.set('category', q.category);166    const s = p.toString();167    return s ? `/timeline?${s}` : '/timeline';168  },169  compare: (ids?: string[]) => (ids?.length ? `/compare?ids=${ids.map(encodeURIComponent).join(',')}` : '/compare'),170  prices: () => '/prices',171  hardwareFit: (q?: { memory_gb?: number | string; quant?: string; context?: number | string }) => {172    const p = new URLSearchParams();173    if (q?.memory_gb !== undefined && q.memory_gb !== '') p.set('memory_gb', String(q.memory_gb));174    if (q?.quant) p.set('quant', q.quant);175    if (q?.context !== undefined && q.context !== '') p.set('context', String(q.context));176    const s = p.toString();177    return s ? `/hardware/fit?${s}` : '/hardware/fit';178  },179  benchmark: (slug: string, q?: { config?: string; model?: string }) => {180    const p = new URLSearchParams();181    if (q?.config) p.set('config', q.config);182    if (q?.model) p.set('model', q.model);183    const s = p.toString();184    return `/benchmarks/${encodeURIComponent(slug)}${s ? `?${s}` : ''}`;185  },186  diff: (q?: { a?: string; b?: string; scope?: string }) => {187    const p = new URLSearchParams();188    if (q?.a) p.set('a', q.a);189    if (q?.b) p.set('b', q.b);190    if (q?.scope && q.scope !== 'all') p.set('scope', q.scope);191    const s = p.toString();192    return s ? `/diff?${s}` : '/diff';193  },194  graph: (slug: string, depth?: 1 | 2) => `/graph/${encodeURIComponent(slug)}${depth === 2 ? '?depth=2' : ''}`,195  /** Entity page "as of" view (History tab). */196  entityAsOf: (e: { entity_type: string; slug: string }, date: string) => `${routes.entity(e)}?tab=history&asof=${encodeURIComponent(date)}`,197  admin: (section?: string) => (section ? `/admin/${section}` : '/admin'),198  explore: () => '/explore',199  exploreType: (t: string) => `/explore/${encodeURIComponent(t)}`,200  methodology: () => '/methodology',201  sources: () => '/sources',202  about: () => '/about',203  developers: () => '/developers',204  bot: () => '/bot',205  listing: (type: string) => {206    const p = TYPE_PATH[type];207    return p ? `/${p}` : `/explore/${encodeURIComponent(type)}`;208  },209  /** Canonical URL of any entity: /models/<slug>, /companies/<slug>, … or /explore/<type>/<slug>. */210  entity: (e: { entity_type: string; slug: string }) => {211    const p = TYPE_PATH[e.entity_type];212    return p ? `/${p}/${encodeURIComponent(e.slug)}` : `/explore/${encodeURIComponent(e.entity_type)}/${encodeURIComponent(e.slug)}`;213  },214};215216export const exploreNav = [217  { href: '/models', label: 'Models', type: 'model' },218  { href: '/companies', label: 'Companies', type: 'company' },219  { href: '/papers', label: 'Research', type: 'paper' },220  { href: '/providers', label: 'Providers & Pricing', type: 'provider' },221  { href: '/benchmarks', label: 'Benchmarks', type: 'benchmark' },222  { href: '/hardware', label: 'Hardware', type: 'hardware' },223  { href: '/frameworks', label: 'Frameworks', type: 'framework' },224  { href: '/datasets', label: 'Datasets', type: 'dataset' },225  { href: '/tools', label: 'Tools', type: 'tool' },226];227export const primaryNav = [228  { href: '/changes', label: 'Changes' },229  { href: '/timeline', label: 'Timeline' },230  { href: '/prices', label: 'Prices' },231  { href: '/compare', label: 'Compare' },232  { href: '/developers', label: 'Developers' },233];234export const moreNav = [235  { href: '/about', label: 'About' },236  { href: '/methodology', label: 'Methodology' },237  { href: '/sources', label: 'Sources' },238  { href: '/diff', label: 'Diff two dates' },239  { href: '/hardware/fit', label: 'Hardware fit' },240  { href: '/developers', label: 'API & Developers' },241  { href: '/bot', label: 'AIAtlasBot' },242];243244/** Change-event vocabulary (sdk/facts.py MATERIAL_PROPERTIES + writer): label, colour family. */245export const EVENT_TYPE_LABELS: Record<string, string> = {246  NEW_MODEL: 'New model',247  NEW_COMPANY: 'New company',248  NEW_PROVIDER: 'New provider',249  NEW_PAPER: 'New paper',250  NEW_DATASET: 'New dataset',251  NEW_BENCHMARK: 'New benchmark',252  NEW_FRAMEWORK: 'New framework',253  NEW_HARDWARE: 'New hardware',254  NEW_TOOL: 'New tool',255  NEW_REPOSITORY: 'New repository',256  NEW_ORGANIZATION: 'New organization',257  NEW_ENTITY: 'New entity',258  MODEL_UPDATED: 'Model updated',259  PRICE_CHANGED: 'Price changed',260  PROVIDER_LISTED: 'Listed by provider',261  CONTEXT_CHANGED: 'Context window changed',262  MAX_OUTPUT_CHANGED: 'Max output changed',263  STATUS_CHANGED: 'Status changed',264  LICENSE_CHANGED: 'License changed',265  OPENNESS_CHANGED: 'Openness changed',266  PARAMETERS_CHANGED: 'Parameters changed',267  RELEASE_DATE_CHANGED: 'Release date changed',268  KNOWLEDGE_CUTOFF_CHANGED: 'Knowledge cutoff changed',269  CAPABILITIES_CHANGED: 'Capabilities changed',270  RATE_LIMIT_CHANGED: 'Rate limit changed',271  SPEC_CHANGED: 'Spec changed',272  DEPRECATION_ANNOUNCED: 'Deprecation announced',273  RETIREMENT_ANNOUNCED: 'Retirement announced',274  BENCHMARK_RESULT: 'Benchmark result',275  BENCHMARK_UPDATED: 'Benchmark updated',276  ANNOUNCEMENT: 'Announcement',277  RELEASE: 'Release',278  VERSION_RELEASED: 'Version released',279  PROPERTY_CHANGED: 'Property changed',280  DOCUMENT_CHANGED: 'Document changed',281};282export function eventLabel(t: string): string {283  return EVENT_TYPE_LABELS[t] ?? t.toLowerCase().replace(/_/g, ' ').replace(/^\w/, (c) => c.toUpperCase());284}285/** Colour family for an event type: accent-2 (amber) for money, positive for new, warning/danger for deprecations. */286export function eventTone(t: string): 'new' | 'price' | 'warn' | 'danger' | 'bench' | 'neutral' {287  if (t.startsWith('NEW_') || t === 'RELEASE' || t === 'VERSION_RELEASED') return 'new';288  if (t === 'PRICE_CHANGED' || t === 'PROVIDER_LISTED') return 'price';289  if (t === 'DEPRECATION_ANNOUNCED') return 'warn';290  if (t === 'RETIREMENT_ANNOUNCED') return 'danger';291  if (t.startsWith('BENCHMARK')) return 'bench';292  return 'neutral';293}294295export const CATEGORY_LABELS: Record<string, string> = {296  model: 'Models',297  company: 'Companies',298  paper: 'Research',299  dataset: 'Datasets',300  benchmark: 'Benchmarks',301  provider: 'Providers & pricing',302  price: 'Pricing',303  framework: 'Frameworks',304  repository: 'Repositories',305  tool: 'Tools',306  hardware: 'Hardware',307  regulation: 'Regulation',308  incident: 'Incidents',309  release: 'Releases',310  document: 'Documents',311  announcement: 'Announcements',312};313export function categoryLabel(c: string): string {314  return CATEGORY_LABELS[c] ?? c.replace(/_/g, ' ').replace(/^\w/, (x) => x.toUpperCase());315}316317export const IMPORTANCE_LABELS: Record<number, string> = { 0: 'Minor', 1: 'Notable', 2: 'Important', 3: 'Major' };318319/** Human labels for attribute keys (docs/CONNECTORS.md property vocabulary). */320export const PROPERTY_LABELS: Record<string, string> = {321  family: 'Family',322  version: 'Version',323  release_date: 'Release date',324  status: 'Status',325  openness: 'Openness',326  license: 'License',327  architecture: 'Architecture',328  parameter_count: 'Parameters',329  active_parameter_count: 'Active parameters',330  is_moe: 'Mixture of experts',331  modalities: 'Modalities',332  modalities_input: 'Input modalities',333  modalities_output: 'Output modalities',334  context_length: 'Context window',335  max_output_tokens: 'Max output',336  knowledge_cutoff: 'Knowledge cutoff',337  training_data_cutoff: 'Training data cutoff',338  languages: 'Languages',339  tool_calling: 'Tool calling',340  structured_output: 'Structured output',341  reasoning: 'Reasoning',342  vision: 'Vision',343  audio: 'Audio',344  fine_tuning_available: 'Fine-tuning available',345  tokenizer: 'Tokenizer',346  api_model_id: 'API model id',347  api_alias: 'API alias',348  official_url: 'Official page',349  model_card_url: 'Model card',350  paper_url: 'Paper',351  repository_url: 'Repository',352  hf_repo: 'Hugging Face repo',353  pipeline_tag: 'Pipeline tag',354  base_model: 'Base model',355  quantization: 'Quantization',356  quant_format: 'Quantization format',357  file_size_gb: 'File size',358  deprecation_date: 'Deprecation date',359  retirement_date: 'Retirement date',360  retirement_tentative: 'Retirement tentative',361  'metric.downloads': 'Downloads',362  'metric.likes': 'Likes',363  'metric.stars': 'Stars',364  'metric.forks': 'Forks',365  country: 'Country',366  headquarters: 'Headquarters',367  founded: 'Founded',368  website: 'Website',369  domains: 'Domains',370  hf_org: 'Hugging Face org',371  github_org: 'GitHub org',372  org_kind: 'Kind',373  legal_name: 'Legal name',374  founders: 'Founders',375  leadership: 'Leadership',376  employee_count: 'Employees',377  pricing_url: 'Pricing page',378  docs_url: 'Documentation',379  regions: 'Regions',380  features: 'Features',381  authors: 'Authors',382  published_at: 'Published',383  updated_at: 'Updated',384  abstract: 'Abstract',385  arxiv_id: 'arXiv id',386  doi: 'DOI',387  categories: 'Categories',388  primary_category: 'Primary category',389  pdf_url: 'PDF',390  code_url: 'Code',391  venue: 'Venue',392  category: 'Category',393  task: 'Task',394  metric: 'Metric',395  unit: 'Unit',396  creator: 'Creator',397  paper: 'Paper',398  known_limitations: 'Known limitations',399  methodology: 'Methodology',400  kind: 'Kind',401  memory_gb: 'Memory',402  memory_type: 'Memory type',403  memory_bandwidth_gbs: 'Memory bandwidth',404  tdp_watts: 'TDP',405  runtimes: 'Runtimes',406  manufacturer: 'Manufacturer',407  spec_url: 'Spec sheet',408  price_usd: 'Price (USD)',409  compute_fp16_tflops: 'FP16 compute',410  latest_version: 'Latest version',411  latest_release_at: 'Latest release',412  language: 'Language',413  topics: 'Topics',414  pypi: 'PyPI',415  modality: 'Modality',416  size: 'Size',417  publisher: 'Publisher',418  description: 'Description',419};420export function propertyLabel(k: string): string {421  return PROPERTY_LABELS[k] ?? k.replace(/^metric\./, '').replace(/_/g, ' ').replace(/^\w/, (c) => c.toUpperCase());422}423/** Attribute keys that are URLs (rendered as links). */424export const URL_KEYS = new Set(['official_url', 'model_card_url', 'paper_url', 'repository_url', 'website', 'pricing_url', 'docs_url', 'pdf_url', 'code_url', 'spec_url']);425/** Soft / long text properties rendered as prose, not in the spec table. */426export const PROSE_KEYS = new Set(['description', 'abstract', 'summary', 'tagline', 'availability_note', 'notes', 'training_data_notes', 'safety_notes', 'hardware_requirements', 'known_limitations', 'methodology']);427428export const PREDICATE_LABELS: Record<string, { out: string; in: string }> = {429  develops: { out: 'Develops', in: 'Developed by' },430  owns: { out: 'Owns', in: 'Owned by' },431  operates: { out: 'Operates', in: 'Operated by' },432  available_through: { out: 'Available through', in: 'Serves' },433  evaluated_on: { out: 'Evaluated on', in: 'Evaluated models' },434  described_by: { out: 'Described by', in: 'Describes' },435  derived_from: { out: 'Derived from', in: 'Derivatives' },436  fine_tuned_from: { out: 'Fine-tuned from', in: 'Fine-tunes' },437  quantized_from: { out: 'Quantized from', in: 'Quantizations' },438  distilled_from: { out: 'Distilled from', in: 'Distillations' },439  merged_from: { out: 'Merged from', in: 'Merged into' },440  superseded_by: { out: 'Superseded by', in: 'Supersedes' },441  runs_on: { out: 'Runs on', in: 'Runs' },442  uses: { out: 'Uses', in: 'Used by' },443  manufactures: { out: 'Manufactures', in: 'Manufactured by' },444  funded_by: { out: 'Funded by', in: 'Funds' },445  acquired: { out: 'Acquired', in: 'Acquired by' },446  authored: { out: 'Authored', in: 'Authors' },447  works_at: { out: 'Works at', in: 'People' },448  uses_dataset: { out: 'Trained on', in: 'Used to train' },449  evaluates_on: { out: 'Evaluates on', in: 'Evaluated by' },450  integrates: { out: 'Integrates', in: 'Integrated by' },451  published_by: { out: 'Published by', in: 'Publications' },452};453export function predicateLabel(p: string, direction: 'out' | 'in'): string {454  return PREDICATE_LABELS[p]?.[direction] ?? p.replace(/_/g, ' ').replace(/^\w/, (c) => c.toUpperCase());455}456457export const OPENNESS_LABELS: Record<string, string> = { 'open-weights': 'Open weights', 'open-source': 'Open source', proprietary: 'Proprietary', restricted: 'Restricted' };458export const STATUS_LABELS: Record<string, string> = {459  active: 'Active',460  preview: 'Preview',461  deprecated: 'Deprecated',462  retired: 'Retired',463  announced: 'Announced',464  'limited-availability': 'Limited availability',465  unknown: 'Unknown',466};467export const TIER_LABELS: Record<number, string> = { 1: 'Official', 2: 'Quality secondary', 3: 'Community', 4: 'Unverified' };468469export const EXAMPLE_QUERIES = [470  'open models with more than 100B parameters',471  'cheapest model with 1M context',472  'models released in 2026',473  'Anthropic',474  'GPQA leaderboard',475  'MoE models with 128k context',476];477
Files · 7456f4abc11ceb761f1f60861a5d96e6ddc93ef4
Menu

Theme