1/**2 * Types mirroring docs/API.md (contract v1). Postgres aggregates may arrive as strings → `Num`; always go through `num()`/`fmt*`.3 * Unknown/extra keys are tolerated: the web layer only reads what it renders.4 */56export type Num = number | string | null;7export type Org = { id: string; slug: string; name: string } | null;89export type Quality = { score?: number; completeness?: number; primary_source_ratio?: number; freshness?: number; source_count?: number; conflicts?: number };10export type Counts = { relations?: number; events?: number; claims?: number };1112export interface EntitySummary {13 id: string;14 entity_type: string;15 slug: string;16 name: string;17 description: string | null;18 status: string;19 organization: Org;20 attributes: Record<string, unknown>;21 quality: Quality;22 counts: Counts;23 first_seen_at: string;24 last_seen_at: string;25 updated_at: string;26}2728export interface Page<T> {29 items: T[];30 total: number;31 limit: number;32 offset: number;33}3435export type ProvenanceEntry = {36 source_id: string | null;37 source_name?: string;38 url: string | null;39 observed_at: string;40 tier: number;41 confidence: string;42 extractor: string;43 unit?: string;44};45export type Provenance = Record<string, ProvenanceEntry>;4647export type Importance = 0 | 1 | 2 | 3;4849export interface ChangeEvent {50 id: string;51 event_type: string;52 category: string;53 property: string | null;54 old_value: unknown;55 new_value: unknown;56 summary: string;57 importance: Importance;58 observed_at: string;59 effective_at: string | null;60 source_url: string | null;61 connector_name: string | null;62 entity: EntitySummary | null;63 meta: Record<string, unknown>;64}6566export interface Price {67 id: string;68 model: EntitySummary;69 provider: EntitySummary;70 provider_model_id: string | null;71 input_per_mtok: Num;72 output_per_mtok: Num;73 cached_input_per_mtok: Num;74 cache_write_per_mtok: Num;75 batch_input_per_mtok: Num;76 batch_output_per_mtok: Num;77 per_image: Num;78 currency: string;79 context_length: Num;80 max_output_tokens: Num;81 features: Record<string, unknown>;82 observed_at: string;83 valid_from: string;84 valid_to: string | null;85 source_url: string | null;86 tier: number;87}8889export interface BenchmarkResult {90 id: string;91 model: EntitySummary;92 benchmark: EntitySummary;93 score: number;94 metric: string | null;95 unit: string | null;96 higher_is_better: boolean;97 config: Record<string, unknown>;98 evaluated_at: string | null;99 observed_at: string;100 source_url: string | null;101 tier: number;102 confidence: string;103}104105export interface Claim {106 id: string;107 property: string;108 value: unknown;109 unit: string | null;110 tier: number;111 confidence: string;112 status: string;113 extractor: string;114 observed_at: string;115 effective_at: string | null;116 valid_from: string;117 valid_to: string | null;118 source_url: string | null;119 source_name: string | null;120}121122export interface SourceRef {123 source_id: string | null;124 source_name: string | null;125 domain: string | null;126 tier: number | null;127 url: string;128 doc_type: string;129 last_observed_at: string | null;130 snapshots: number;131}132133export interface RelationGroup {134 predicate: string;135 direction: 'out' | 'in';136 items: EntitySummary[];137 total: number;138}139140export interface Lineage {141 ancestors: EntitySummary[];142 descendants: EntitySummary[];143 quantizations: EntitySummary[];144}145146export interface HardwareFitRow {147 hardware: EntitySummary;148 quantization: string;149 estimated_memory_gb: number;150 fits: boolean;151}152153export interface EntityDetail extends EntitySummary {154 provenance: Provenance;155 aliases: string[];156 identifiers: { scheme: string; value: string }[];157 relations: RelationGroup[];158 sources: SourceRef[];159 timeline: ChangeEvent[];160 prices?: Price[];161 price_history?: Price[];162 results?: BenchmarkResult[];163 lineage?: Lineage;164 providers?: EntitySummary[];165 hardware_fit?: HardwareFitRow[];166 models?: Page<EntitySummary>;167 papers?: EntitySummary[];168 repositories?: EntitySummary[];169 // companies listing enrichments may also be present on detail170 model_count?: number;171 paper_count?: number;172}173174export interface Health {175 status: 'ok' | 'degraded' | string;176 version: string;177 db: boolean;178 redis: boolean;179 llm: { available: boolean; reachable?: boolean };180 time: string;181}182183export interface Stats {184 entities: Record<string, Num>;185 entities_total: Num;186 sources: Num;187 connectors: Num;188 connectors_enabled: Num;189 documents: Num;190 snapshots: Num;191 claims: Num;192 claims_current: Num;193 relations: Num;194 change_events: Num;195 change_events_24h: Num;196 change_events_7d: Num;197 benchmark_results: Num;198 prices_current: Num;199 prices_total: Num;200 review_pending: Num;201 llm_jobs: Num;202 llm_tokens: Num;203 last_snapshot_at: string | null;204 last_event_at: string | null;205 first_entity_at: string | null;206 archive: { raw_bytes: Num; raw_files: Num; text_bytes: Num; text_files: Num };207 computed_at: string;208}209210export interface StatsHistory {211 items: { day: string; counts: Record<string, Num> }[];212}213214/** Output of services.search.compile_query — shape is loose; we render what we recognise. */215export interface CompiledQuery {216 text?: string | null;217 q?: string | null;218 entity_type?: string | null;219 type?: string | null;220 filters?: Record<string, unknown>;221 [k: string]: unknown;222}223224export interface SearchPayload {225 query: CompiledQuery;226 items: (EntitySummary & { rank: number })[];227 total: number;228}229230export interface Suggestion {231 id: string;232 entity_type: string;233 slug: string;234 name: string;235 organization_name: string | null;236}237238export interface FacetValue {239 value: string;240 count: Num;241}242export interface OrgFacet {243 slug: string;244 name: string;245 count: Num;246}247export interface ModelFacets {248 organizations?: OrgFacet[];249 openness?: FacetValue[];250 modalities?: FacetValue[];251 families?: FacetValue[];252 years?: FacetValue[];253 licenses?: FacetValue[];254 status?: FacetValue[];255}256export type ModelsPage = Page<EntitySummary> & { facets?: ModelFacets };257258export type CompanyRow = EntitySummary & { model_count: Num; paper_count: Num };259export interface CompanyFacets {260 countries?: FacetValue[];261 kinds?: FacetValue[];262}263export type CompaniesPage = Page<CompanyRow> & { facets?: CompanyFacets };264265export type ProviderRow = EntitySummary & { model_count: Num; price_count: Num; min_input_per_mtok: Num; min_output_per_mtok: Num };266export type BenchmarkRow = EntitySummary & { result_count: Num; model_count: Num; top: { model: EntitySummary; score: number } | null };267268export interface PriceIndexPoint {269 day: string;270 median_input: Num;271 median_output: Num;272 min_input: Num;273 max_input?: Num;274 models: number;275 offers?: number;276}277export interface PriceIndex {278 days?: number;279 note?: string;280 series: PriceIndexPoint[];281 movers: ChangeEvent[];282}283284export interface HardwareFitItem {285 model: EntitySummary;286 parameter_count: number;287 estimated_memory_gb: number;288 fits: boolean;289 headroom_gb: number;290 quantization: string;291 note: string;292}293export interface HardwareFit {294 inputs: Record<string, unknown>;295 estimated?: boolean;296 counts?: { fits?: Num; evaluated?: Num };297 assumptions: string[];298 items: HardwareFitItem[];299}300301export interface ExploreType {302 entity_type: string;303 count: Num;304 label: string;305}306307export interface DailyDigest {308 date: string;309 counts: Record<string, Num>;310 sections: { category: string; label: string; items: ChangeEvent[] }[];311 new_models: EntitySummary[];312}313314export interface ChangeCategories {315 items: { category: string; event_type: string; count: Num }[];316}317318export interface TimelinePayload {319 items: { month: string; count?: number; events: ChangeEvent[] }[];320 total?: number;321}322323export interface CompareDimension {324 key: string;325 label: string;326 unit?: string;327 kind: 'number' | 'text' | 'list' | 'bool' | 'date';328 /** Where the value comes from: `attr` (claims) · `prices` · `results` (benchmark) — informational. */329 source?: string;330}331export interface ComparePayload {332 entity_type: string;333 dimensions: CompareDimension[];334 items: { entity: EntitySummary; values: Record<string, unknown>; provenance: Provenance; prices?: Price[]; results?: BenchmarkResult[] }[];335}336337export interface DiffPayload {338 a: string;339 b: string;340 scope?: Record<string, unknown> | string;341 new_entities: EntitySummary[];342 gone_entities: EntitySummary[];343 property_changes: ChangeEvent[];344 price_changes: ChangeEvent[];345 benchmark_changes: ChangeEvent[];346 counts: Record<string, Num>;347}348349export interface ConnectorHealth {350 name: string;351 label: string;352 health: string;353 last_success_at: string | null;354 interval_seconds: Num;355}356export interface SourceRow {357 key: string;358 name: string;359 domain: string;360 tier: number;361 kind: string;362 category: string;363 organization: Org;364 enabled: boolean;365 documents: Num;366 last_crawled_at: string | null;367 connectors: ConnectorHealth[];368}369370export interface MetricDefinition {371 key?: string;372 name?: string;373 label?: string;374 description?: string;375 formula?: string;376 version?: string | number;377 unit?: string;378 [k: string]: unknown;379}380export interface Methodology {381 metrics: MetricDefinition[];382 confidence_levels: Record<string, string> | { key: string; label?: string; description?: string }[] | string[];383 tiers: Record<string, string> | { tier: number; label?: string; description?: string }[];384 event_types: Record<string, string> | { event_type: string; category?: string; label?: string; description?: string; importance?: number }[] | string[];385 extractors: Record<string, string> | { key?: string; name?: string; description?: string }[] | string[];386}387388export type TrendingRow = EntitySummary & { views: Num };389390export interface SitemapPayload {391 items: { slug: string; entity_type: string; updated_at: string }[];392 total: number;393}394395export interface GraphNode {396 id: string;397 slug: string;398 name: string;399 entity_type: string;400 organization_name?: string | null;401}402export interface GraphEdge {403 source: string;404 target: string;405 predicate: string;406}407export interface GraphPayload {408 root?: string;409 nodes: GraphNode[];410 edges: GraphEdge[];411}412413export interface AsOfPayload {414 id?: string;415 slug?: string;416 name?: string;417 entity_type?: string;418 existed: boolean;419 first_seen_at: string | null;420 date: string;421 attributes: Record<string, unknown>;422 claims: Claim[];423}424