/** Static catalogue of the public API 1.1 routes (docs/API.md) for the request builder. No endpoint here is invented. */ export type RouteParam = { name: string; example?: string; hint?: string; path?: boolean }; export type RouteDef = { id: string; method: 'GET'; path: string; group: string; summary: string; params: RouteParam[]; returns: string }; export const ROUTES: RouteDef[] = [ { id: 'health', method: 'GET', path: '/health', group: 'Meta', summary: 'Service status', params: [], returns: '{ status: "ok"|"degraded", version, api_version: "1.1", db, redis, llm: { available, reachable? }, time }' }, { id: 'stats', method: 'GET', path: '/stats', group: 'Meta', summary: 'Live counters with definitions', params: [], returns: '{ entities: Record, entities_total, organizations_total, artifacts, model_families, change_events_live_24h, definitions: Record, … computed_at }' }, { id: 'search', method: 'GET', path: '/search', group: 'Search', summary: 'Compiler v2 + ranked results', params: [{ name: 'q', example: 'open reasoning models over 30B released in 2026' }, { name: 'type', hint: 'entity type' }, { name: 'limit', example: '10' }, { name: 'offset' }], returns: '{ query: { compiled: { filter, label, value, source_span }[], sort, residual, unrecognised, semantic, version: 2 }, items: (EntitySummary & { rank })[], total }' }, { id: 'suggest', method: 'GET', path: '/search/suggest', group: 'Search', summary: '≤ 8 prefix suggestions', params: [{ name: 'q', example: 'clau' }], returns: '{ items: { id, entity_type, slug, name, organization_name }[] }' }, { id: 'entity', method: 'GET', path: '/entities/{slug}', group: 'Entities', summary: 'Full detail with provenance', params: [{ name: 'slug', example: 'claude-opus-5', path: true }], returns: 'EntityDetail: attributes, provenance, aliases, identifiers, relations[], sources[], timeline[] + type blocks (prices, results, lineage, family, artifacts, deployments, identity, licence, openness, version_history, benchmarks…)' }, { id: 'models', method: 'GET', path: '/models', group: 'Entities', summary: 'Canonical model universe', params: [{ name: 'q' }, { name: 'org', example: 'anthropic' }, { name: 'family' }, { name: 'openness', example: 'open-weights' }, { name: 'modality' }, { name: 'status' }, { name: 'min_params', example: '30000000000' }, { name: 'max_params' }, { name: 'min_context' }, { name: 'year_from', example: '2026' }, { name: 'year_to' }, { name: 'license' }, { name: 'reasoning', hint: '0|1' }, { name: 'trust', hint: 'high,medium,low' }, { name: 'include', hint: 'artifacts' }, { name: 'sort', hint: 'release|params|name|quality|cheapest' }, { name: 'order' }, { name: 'limit', example: '5' }, { name: 'offset' }, { name: 'facets', example: '1' }], returns: 'Page & { universe, facets? }' }, { id: 'model-diff', method: 'GET', path: '/models/{a}/diff/{b}', group: 'Entities', summary: 'Differing dimensions of two models', params: [{ name: 'a', example: 'claude-opus-5', path: true }, { name: 'b', example: 'claude-sonnet-5', path: true }], returns: '{ a, b, dimensions: (Dimension & { a, b, delta })[], comparability, note }' }, { id: 'companies', method: 'GET', path: '/companies', group: 'Entities', summary: 'Organizations (companies, labs, universities)', params: [{ name: 'q' }, { name: 'country' }, { name: 'kind' }, { name: 'sort', example: 'models' }, { name: 'limit', example: '5' }, { name: 'facets', example: '1' }], returns: 'Page; total = /stats.organizations_total' }, { id: 'papers', method: 'GET', path: '/papers', group: 'Entities', summary: 'Papers', params: [{ name: 'q' }, { name: 'category', example: 'cs.CL' }, { name: 'org' }, { name: 'since' }, { name: 'until' }, { name: 'sort', example: 'published' }, { name: 'limit', example: '5' }], returns: 'Page' }, { id: 'families', method: 'GET', path: '/families', group: 'Entities', summary: 'Model families', params: [{ name: 'q' }, { name: 'org', example: 'anthropic' }, { name: 'sort', hint: 'models|name|last_release' }, { name: 'limit', example: '5' }], returns: 'Page<{ id, slug, name, canonical, organization, model_count, first_release, last_release, param_range, modalities, licenses, benchmark_best }> & { note }' }, { id: 'family', method: 'GET', path: '/families/{slug}', group: 'Entities', summary: 'Family detail with members and lineage', params: [{ name: 'slug', example: 'claude', path: true }, { name: 'limit' }], returns: '{ …aggregates, members: { model, key_facts, benchmark_ranks }[], artifacts_count, providers, lineage, timeline, note }' }, { id: 'licenses', method: 'GET', path: '/licenses', group: 'Entities', summary: 'Licence ontology', params: [], returns: '{ items: (LicenseInfo & { aliases, models })[], total, categories, unclassified, note }' }, { id: 'timeline-entity', method: 'GET', path: '/entities/{slug}/timeline', group: 'Temporal', summary: 'Entity events (occurred_at order)', params: [{ name: 'slug', example: 'anthropic', path: true }, { name: 'limit', example: '20' }, { name: 'before', hint: 'cursor' }, { name: 'include_backfill', hint: '0|1' }, { name: 'date_field', hint: 'occurred|observed' }], returns: '{ items: ChangeEvent[], next_before, date_field, include_backfill }' }, { id: 'history', method: 'GET', path: '/entities/{slug}/history', group: 'Temporal', summary: 'Claim history of one property', params: [{ name: 'slug', example: 'claude-opus-5', path: true }, { name: 'property', example: 'context_length' }], returns: '{ items: Claim[] } — all statuses, newest first' }, { id: 'asof', method: 'GET', path: '/entities/{slug}/asof', group: 'Temporal', summary: 'Entity as of a date', params: [{ name: 'slug', example: 'claude-opus-5', path: true }, { name: 'date', example: '2026-06-01' }], returns: '{ existed, first_seen_at, date, attributes, claims }' }, { id: 'claims', method: 'GET', path: '/entities/{slug}/claims', group: 'Provenance', summary: 'Claims of an entity', params: [{ name: 'slug', example: 'claude-opus-5', path: true }, { name: 'property' }, { name: 'status', hint: 'current|all|superseded|conflicting|retracted' }, { name: 'limit', example: '10' }], returns: '{ entity, items: (Claim & { snapshot_id, run_id, value_raw })[], total, limit, offset, status }' }, { id: 'provenance', method: 'GET', path: '/entities/{slug}/provenance/{property}', group: 'Provenance', summary: 'Evidence behind one value', params: [{ name: 'slug', example: 'claude-opus-5', path: true }, { name: 'property', example: 'context_length', path: true }], returns: '{ entity, property, value, source: { id, name, domain, url }, tier, confidence, extractor, observed_at, valid_since, claim_id, run_id, snapshot: { id, observed_at, document_url, title, archived }, conflicts, history_count }' }, { id: 'claim', method: 'GET', path: '/claims/{id}', group: 'Provenance', summary: 'One claim with its lifecycle', params: [{ name: 'id', example: 'claim_…', path: true }], returns: '{ claim, entity, property, chain: { previous, superseding, conflicting, history_count }, source, extractor, run_id, evidence, note }' }, { id: 'graph', method: 'GET', path: '/entities/{slug}/graph', group: 'Graph', summary: 'Capped neighbourhood (v1)', params: [{ name: 'slug', example: 'claude-opus-5', path: true }, { name: 'depth', example: '1' }, { name: 'limit', example: '80' }], returns: '{ root, nodes: { id, slug, name, entity_type, organization_name }[], edges: { source, target, predicate }[] }' }, { id: 'graph-explore', method: 'GET', path: '/graph/explore', group: 'Graph', summary: 'Typed explorer, 7 modes', params: [{ name: 'node', example: 'anthropic' }, { name: 'mode', example: 'company', hint: 'lineage|research|company|benchmark|dataset|provider|hardware' }, { name: 'depth', example: '1' }, { name: 'limit', example: '150' }], returns: '{ root, mode, depth, predicates, nodes: { id, slug, name, entity_type, org, org_slug, level, artifact_kind, attributes }[], edges: { source, target, predicate, attributes, tier }[], truncated, counts }' }, { id: 'changes', method: 'GET', path: '/changes', group: 'Temporal', summary: 'Change feed (cursor)', params: [{ name: 'category' }, { name: 'type', example: 'PRICE_CHANGED' }, { name: 'entity_type' }, { name: 'importance_min', example: '2' }, { name: 'since' }, { name: 'until' }, { name: 'q' }, { name: 'entity' }, { name: 'limit', example: '10' }, { name: 'before', hint: 'cursor' }, { name: 'include_backfill', hint: '0|1' }, { name: 'date_field', hint: 'occurred|observed' }], returns: 'Page & { next_before, date_field, include_backfill }' }, { id: 'daily', method: 'GET', path: '/changes/daily', group: 'Temporal', summary: 'Today in AI 2.0', params: [{ name: 'date', example: new Date().toISOString().slice(0, 10) }, { name: 'per_section', example: '5' }, { name: 'include_backfill', hint: '0|1' }], returns: '{ date, counts, total, sections, today: { key, label, items: (ChangeEvent & { sources, documents, grouped_events })[], total }[], new_models, labels, backfill_excluded, previous_day, next_day, note }' }, { id: 'timeline', method: 'GET', path: '/timeline', group: 'Temporal', summary: 'Events grouped by month', params: [{ name: 'entity' }, { name: 'year', example: '2026' }, { name: 'category' }, { name: 'importance_min' }, { name: 'limit', example: '50' }, { name: 'include_backfill', hint: '0|1' }], returns: '{ items: { month, count, events }[], total, date_field, include_backfill }' }, { id: 'diff', method: 'GET', path: '/diff', group: 'Temporal', summary: 'Diff two dates', params: [{ name: 'a', example: new Date(Date.now() - 7 * 86400000).toISOString().slice(0, 10) }, { name: 'b', example: new Date().toISOString().slice(0, 10) }, { name: 'scope', example: 'all', hint: 'all|models|org:|family:' }, { name: 'limit', example: '5' }, { name: 'include_backfill', hint: '0|1' }], returns: '{ a, b, scope, new_entities, gone_entities, property_changes, price_changes, benchmark_changes, new_benchmark_leaders, provider_changes, hardware_changes, context_changes, retired_models, counts, note }' }, { id: 'time-machine', method: 'GET', path: '/time-machine', group: 'Temporal', summary: 'The atlas as of a date', params: [{ name: 'date', example: '2025-06-01' }, { name: 'scope', example: 'models', hint: 'models|prices|benchmarks|hardware|all' }, { name: 'limit', example: '5' }], returns: '{ date, scope, first_entity_at, reconstructed, note, models?: { items: { model, attributes_as_of, observed_then, reconstructed }[], total }, prices?, benchmarks?: { leaders }, hardware? }' }, { id: 'pulse', method: 'GET', path: '/pulse', group: 'Intelligence', summary: 'Live activity counters', params: [{ name: 'days', example: '7' }], returns: '{ days, since, until, counters: Record, note }' }, { id: 'frontier', method: 'GET', path: '/frontier', group: 'Intelligence', summary: 'Who leads on what', params: [{ name: 'limit', example: '5' }], returns: '{ latest_major_models, benchmark_frontier, price_frontier, context_frontier, open_weight_frontier, efficiency_frontier, agentic_frontier, multimodal_frontier, recent_frontier_movements, generated_at, methodology }' }, { id: 'trending', method: 'GET', path: '/trending', group: 'Intelligence', summary: 'Most viewed / most changed', params: [{ name: 'kind', example: 'most_changed', hint: 'views|most_changed|new_listings|new_results' }, { name: 'days', example: '7' }, { name: 'limit', example: '5' }, { name: 'type' }], returns: '{ days, kind, items, definition }' }, { id: 'benchmarks', method: 'GET', path: '/benchmarks', group: 'Benchmarks', summary: 'Benchmarks with leaders', params: [{ name: 'category' }], returns: '{ items: { slug, name, category, metric, result_count, model_count, leader, groups, trust_mix }[], total, note }' }, { id: 'leaderboard', method: 'GET', path: '/benchmarks/{slug}/leaderboard', group: 'Benchmarks', summary: 'One row per canonical model', params: [{ name: 'slug', example: 'gpqa-diamond', path: true }, { name: 'metric' }, { name: 'config_key' }, { name: 'trust' }, { name: 'org' }, { name: 'comparable_only', hint: '0|1' }, { name: 'limit', example: '10' }], returns: '{ benchmark, group, groups, items: LeaderboardRow[], total, comparable_only, filters, history_available, methodology }' }, { id: 'matrix', method: 'GET', path: '/benchmarks/matrix', group: 'Benchmarks', summary: 'Models × benchmarks', params: [{ name: 'benchmarks' }, { name: 'models' }, { name: 'org' }, { name: 'family' }, { name: 'limit', example: '10' }, { name: 'min_cells', example: '3' }], returns: '{ columns, rows: { model, cells, n_cells, mean_rank }[], total_rows, methodology }' }, { id: 'prices', method: 'GET', path: '/prices', group: 'Prices', summary: 'Current offers', params: [{ name: 'model' }, { name: 'provider' }, { name: 'org' }, { name: 'family' }, { name: 'sort', hint: 'cheapest_frontier…' }, { name: 'current', example: '1' }, { name: 'limit', example: '5' }], returns: 'Page' }, { id: 'price-index', method: 'GET', path: '/prices/index', group: 'Prices', summary: 'AI Price Index', params: [{ name: 'days', example: '90' }], returns: '{ days, series[], movers, cheapest_frontier, distribution, new_listings_30d, delistings_30d, price_changes_30d, frontier, methodology, note }' }, { id: 'deployments', method: 'GET', path: '/deployments', group: 'Prices', summary: 'Model × provider offers', params: [{ name: 'model' }, { name: 'provider' }, { name: 'org' }, { name: 'current', example: '1' }, { name: 'sort' }, { name: 'limit', example: '5' }], returns: 'Page & { next_before, current }' }, { id: 'cost', method: 'GET', path: '/cost', group: 'Prices', summary: 'Workload cost across providers', params: [{ name: 'model', example: 'claude-opus-5' }, { name: 'input_tokens', example: '1000' }, { name: 'output_tokens', example: '500' }, { name: 'requests_per_day', example: '1000' }, { name: 'cached_share' }, { name: 'batch', hint: '0|1' }], returns: '{ model, inputs, items: { deployment, cost: { per_request, daily, monthly, annual, … } }[], total, currency, methodology, note }' }, { id: 'fit', method: 'GET', path: '/hardware/fit', group: 'Hardware', summary: 'ESTIMATED fit', params: [{ name: 'memory_gb', example: '64' }, { name: 'quant', example: '4bit' }, { name: 'context', example: '8192' }, { name: 'limit', example: '5' }], returns: '{ inputs, estimated: true, assumptions[], counts, items[] }' }, { id: 'run-locally', method: 'GET', path: '/run-locally', group: 'Hardware', summary: 'What fits your machine', params: [{ name: 'memory_gb', example: '32' }, { name: 'quant', example: '4bit' }, { name: 'context', example: '8192' }, { name: 'platform', hint: 'apple|nvidia|amd|any' }, { name: 'limit', example: '5' }], returns: '{ inputs, estimated: true, assumptions, counts, items: { model, fit, artifacts, artifact_count }[], note }' }, { id: 'open', method: 'GET', path: '/open', group: 'Intelligence', summary: 'Open-weight universe', params: [{ name: 'sort', hint: 'release|params|context|rank|name|downloads' }, { name: 'days', example: '30' }, { name: 'limit', example: '5' }], returns: 'Page<{ model, licence, dimensions, best_results, best_rank, hardware_fit, providers, cheapest_output_per_mtok }> & { summary, note }' }, { id: 'find', method: 'GET', path: '/find-a-model', group: 'Intelligence', summary: 'Deterministic finder', params: [{ name: 'use_case', example: 'coding', hint: 'coding|reasoning|agentic|long_context|vision|low_cost|local|embeddings|chat' }, { name: 'deployment', hint: 'local|api|any' }, { name: 'memory_gb' }, { name: 'license', hint: 'commercial|any' }, { name: 'limit', example: '5' }], returns: '{ matches: { model, why[], observed, estimated_fit?, deployments? }[], total, filters_applied, rules, note }' }, { id: 'sources', method: 'GET', path: '/sources', group: 'Meta', summary: 'Sources and connector health', params: [], returns: '{ items: { key, name, domain, tier, kind, category, organization, enabled, documents, snapshots, claims, last_crawled_at, connectors[] }[], total, tiers }' }, { id: 'methodology', method: 'GET', path: '/methodology', group: 'Meta', summary: 'Every vocabulary and rule', params: [], returns: '{ metrics, tiers, confidence_levels, event_types, extractors, openness, trust_levels, comparability, counters, anomaly_checks, event_semantics, hardware_fit, frontier, find_a_model, licence_categories, principles }' }, { id: 'sitemap', method: 'GET', path: '/sitemap', group: 'Meta', summary: 'Sitemap feed', params: [{ name: 'type', example: 'model_family' }, { name: 'limit', example: '5' }, { name: 'offset' }], returns: '{ items: { slug, entity_type, updated_at }[], total }' }, ]; export function buildPath(r: RouteDef, values: Record): string { let path = r.path; const qs = new URLSearchParams(); for (const p of r.params) { const v = (values[p.name] ?? '').trim(); if (p.path) path = path.replace(`{${p.name}}`, encodeURIComponent(v || p.example || '')); else if (v) qs.set(p.name, v); } const s = qs.toString(); return `${path}${s ? `?${s}` : ''}`; }