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%
10.1 KB · 189 lines python
Raw Blame History
1"""Canonical ontology: model hierarchy columns (family / canonical / artifact kind), run ids on every fact for batch rollback,2event semantics (recorded_at, occurred_at, is_backfill, group_key), benchmark comparability (config_key, trust_level, variant,3run_group, is_current), persisted entity-resolution decisions, anomaly flags, admin audit log, quarantined runs, taxonomy mappings.45Additive only — no column is dropped or retyped; existing rows keep their values.67Revision ID: 00038"""9from __future__ import annotations1011from alembic import op1213revision = "0003"14down_revision = "0002"15branch_labels = None16depends_on = None1718SQL = r"""19-- ---------------------------------------------------------------------------------------------- entity hierarchy20alter table entities add column if not exists family_id text references entities(id);21-- artifact → model; folded variant → model22alter table entities add column if not exists canonical_id text references entities(id);23-- checkpoint|quantization|conversion|packaging (artifacts only)24alter table entities add column if not exists artifact_kind text;25-- high|medium|low (how sure we are this row is one real thing)26alter table entities add column if not exists identity_confidence text not null default 'high';27create index if not exists entities_family_idx on entities (family_id) where family_id is not null;28create index if not exists entities_canonical_idx on entities (canonical_id) where canonical_id is not null;29create index if not exists entities_type_slug_idx on entities (entity_type, slug);30create index if not exists entities_type_status_idx on entities (entity_type, status) where merged_into is null;3132-- ---------------------------------------------------------------------------------------------- run ids on facts (batch rollback)33alter table claims add column if not exists run_id text;34alter table relations add column if not exists run_id text;35alter table prices add column if not exists run_id text;36alter table benchmark_results add column if not exists run_id text;37alter table change_events add column if not exists run_id text;38create index if not exists claims_run_idx on claims (run_id) where run_id is not null;39create index if not exists relations_run_idx on relations (run_id) where run_id is not null;40create index if not exists prices_run_idx on prices (run_id) where run_id is not null;41create index if not exists benchmark_results_run_idx on benchmark_results (run_id) where run_id is not null;42create index if not exists change_events_run_idx on change_events (run_id) where run_id is not null;43-- source label when the canonical value differs44alter table claims add column if not exists value_raw text;4546-- ---------------------------------------------------------------------------------------------- event semantics47alter table change_events add column if not exists recorded_at timestamptz not null default now();48alter table change_events add column if not exists is_backfill boolean not null default false;49-- semantic group (one release across documents)50alter table change_events add column if not exists group_key text;51alter table change_events add column if not exists occurred_at timestamptz generated always as (coalesce(effective_at, observed_at)) stored;52create index if not exists change_events_live_idx on change_events (occurred_at desc) where is_backfill = false and event_type <> 'DOCUMENT_CHANGED';53create index if not exists change_events_occurred_idx on change_events (occurred_at desc);54create index if not exists change_events_group_idx on change_events (group_key) where group_key is not null;55create index if not exists change_events_live_importance_idx on change_events (importance desc, occurred_at desc) where is_backfill = false;5657-- ---------------------------------------------------------------------------------------------- benchmark comparability & trust58alter table benchmark_results add column if not exists config_key text;59alter table benchmark_results add column if not exists trust_level text;60alter table benchmark_results add column if not exists variant text;61alter table benchmark_results add column if not exists run_group text;62alter table benchmark_results add column if not exists is_current boolean not null default true;63alter table benchmark_results add column if not exists extractor text not null default 'deterministic';64create index if not exists benchmark_results_current2_idx on benchmark_results (benchmark_id, config_key, is_current) where valid_to is null;65create index if not exists benchmark_results_model_metric_idx on benchmark_results (model_id, benchmark_id, metric) where valid_to is null;6667-- ---------------------------------------------------------------------------------------------- persisted resolution decisions68create table if not exists resolution_decisions (69  id          text primary key,70  a_id        text not null references entities(id) on delete cascade,71  b_id        text not null references entities(id) on delete cascade,72  decision    text not null,                 -- merge|alias|variant_of|family_member|keep_separate|defer73  actor       text not null default 'admin',74  note        text,75  payload     jsonb not null default '{}'::jsonb,76  applied     boolean not null default false,77  created_at  timestamptz not null default now(),78  unique (a_id, b_id, decision)79);80create index if not exists resolution_decisions_pair_idx on resolution_decisions (a_id, b_id);81create index if not exists resolution_decisions_b_idx on resolution_decisions (b_id);8283-- ---------------------------------------------------------------------------------------------- anomaly flags (never deletes)84create table if not exists anomalies (85  id            text primary key,86  entity_id     text references entities(id) on delete cascade,87  check_name    text not null,88  severity      text not null,               -- critical|warning|info89  message       text not null,90  value         jsonb,91  detail        jsonb not null default '{}'::jsonb,92  status        text not null default 'open', -- open|resolved|ignored|fixed93  first_seen_at timestamptz not null default now(),94  last_seen_at  timestamptz not null default now(),95  resolved_at   timestamptz,96  resolution    text,97  dedupe_key    text unique98);99create index if not exists anomalies_status_idx on anomalies (status, severity, last_seen_at desc);100create index if not exists anomalies_entity_idx on anomalies (entity_id);101102-- ---------------------------------------------------------------------------------------------- admin audit log103create table if not exists admin_audit_log (104  id         bigserial primary key,105  actor      text not null default 'admin',106  action     text not null,107  target     text,108  payload    jsonb not null default '{}'::jsonb,109  ip         text,110  created_at timestamptz not null default now()111);112create index if not exists admin_audit_log_created_idx on admin_audit_log (created_at desc);113114-- ---------------------------------------------------------------------------------------------- quarantined runs (held facts awaiting review)115create table if not exists quarantined_runs (116  id             text primary key,117  run_id         text,118  connector_name text not null,119  reason         text not null,120  stats          jsonb not null default '{}'::jsonb,   -- baseline vs observed counts121  facts          jsonb not null default '[]'::jsonb,   -- serialised Facts objects, written on release122  status         text not null default 'pending',      -- pending|released|discarded123  created_at     timestamptz not null default now(),124  resolved_at    timestamptz,125  resolved_by    text126);127create index if not exists quarantined_runs_status_idx on quarantined_runs (status, created_at desc);128129-- ---------------------------------------------------------------------------------------------- taxonomy mappings observed (raw → canonical)130create table if not exists taxonomy_mappings (131  domain     text not null,                  -- license|openness|modality|status|org_kind|hardware_kind|framework_kind|metric132  raw        text not null,133  canonical  text,134  count      integer not null default 1,135  first_seen_at timestamptz not null default now(),136  last_seen_at  timestamptz not null default now(),137  primary key (domain, raw)138);139140-- ---------------------------------------------------------------------------------------------- connector run baselines (anomaly detector)141alter table connector_runs add column if not exists quarantined boolean not null default false;142alter table connector_runs add column if not exists baseline jsonb;143-- rolling medians of records/prices/results144alter table connectors add column if not exists baseline jsonb;145146-- ---------------------------------------------------------------------------------------------- search vector: include aliases of family147create or replace function entities_search_update() returns trigger language plpgsql as $$148begin149  new.search :=150    setweight(to_tsvector('simple', coalesce(new.canonical_name, '')), 'A') ||151    setweight(to_tsvector('simple', coalesce(new.attributes->>'family', '')), 'B') ||152    setweight(to_tsvector('simple', coalesce(new.attributes->>'api_model_id', '')), 'B') ||153    setweight(to_tsvector('simple', coalesce(new.entity_type, '')), 'C') ||154    setweight(to_tsvector('english', left(coalesce(new.description, ''), 4000)), 'C');155  new.updated_at := now();156  return new;157end $$;158"""159160161def upgrade() -> None:162    for statement in _split(SQL):163        op.execute(statement)164165166def downgrade() -> None:167    raise RuntimeError("AI Atlas migrations are forward-only: historical data is never disposable")168169170def _split(sql: str) -> list[str]:171    out: list[str] = []172    buf: list[str] = []173    in_dollar = False174    for line in sql.splitlines():175        stripped = line.strip()176        if stripped.count("$$") % 2 == 1:177            in_dollar = not in_dollar178        buf.append(line)179        if not in_dollar and stripped.endswith(";"):180            body = [ln for ln in buf if not ln.strip().startswith("--") or in_dollar]181            stmt = "\n".join(body).strip()182            if stmt:183                out.append(stmt)184            buf = []185    tail = "\n".join(ln for ln in buf if not ln.strip().startswith("--")).strip()186    if tail:187        out.append(tail)188    return out189