SPB Git

spb/airiskindex Public

The most methodologically rigorous, fully transparent AI job-exposure index.

TypeScript 88% Python 6.1% SQL 2.7% CSS 1.2% JavaScript 0.9% Shell 0.8%
10.7 KB · 262 lines typescript
Raw Blame History
1//  File:    seed.ts2//  Path:    packages/db/prisma/seed.ts3//  Project: AI Risk Index — airiskindex.io4//  Author:  Simon-Pierre Boucher5//  Contact: contact@spboucher.ai6//  Copyright © 2026 Simon-Pierre Boucher. All rights reserved.7//8//  Description: Demo dataset seed: 10 occupations with synthetic two-model panel ratings.910import { PrismaClient } from "@prisma/client";1112// Demo seed: 10 real O*NET-SOC occupations with plausible hand-authored task13// ratings from a synthetic two-model panel ("demo-panel-a/b"). This exists so14// the site has data before the real O*NET ETL + LLM rating batch runs; the web15// layout shows a "Preview — demonstration dataset" banner, and every score16// still traces to a ScoreRun whose raterModels make the provenance explicit.17// Deterministic on purpose — reseeding yields identical ratings.1819const prisma = new PrismaClient();2021const DIMS = [22  "automatability",23  "feasibility",24  "cost_ratio",25  "barriers",26  "adoption_velocity",27  "augmentation",28] as const;2930type Dim = (typeof DIMS)[number];31type Profile = Record<Dim, number>;3233interface DemoTask {34  id: string;35  statement: string;36  importance: number;37  adjust?: Partial<Profile>;38}3940interface DemoOccupation {41  code: string;42  title: string;43  description: string;44  wageCents: number; // median annual, USD cents45  profile: Profile;46  tasks: DemoTask[];47}4849const p = (50  automatability: number,51  feasibility: number,52  cost_ratio: number,53  barriers: number,54  adoption_velocity: number,55  augmentation: number,56): Profile => ({ automatability, feasibility, cost_ratio, barriers, adoption_velocity, augmentation });5758const DEMO: DemoOccupation[] = [59  {60    code: "43-9021.00",61    title: "Data Entry Keyers",62    description:63      "Operate data entry devices to enter, verify and prepare data from source documents.",64    wageCents: 3_717_000_00 / 100,65    profile: p(5, 5, 5, 1, 4, 3),66    tasks: [67      { id: "T1", statement: "Enter account or customer data from source documents within time limits.", importance: 4.6 },68      { id: "T2", statement: "Verify entered data by reviewing, correcting or re-entering it.", importance: 4.2 },69      { id: "T3", statement: "Compile, sort and check accuracy of source documents before entry.", importance: 3.8, adjust: { automatability: -1 } },70    ],71  },72  {73    code: "43-4051.00",74    title: "Customer Service Representatives",75    description:76      "Interact with customers to handle inquiries, complaints and account questions.",77    wageCents: 3_933_000_00 / 100,78    profile: p(4, 4, 4, 2, 4, 4),79    tasks: [80      { id: "T1", statement: "Respond to routine customer inquiries about products, services and accounts.", importance: 4.7 },81      { id: "T2", statement: "Resolve complaints and escalate complex or sensitive cases.", importance: 4.1, adjust: { automatability: -1, barriers: 1 } },82      { id: "T3", statement: "Keep records of customer interactions and actions taken.", importance: 3.6, adjust: { automatability: 1 } },83    ],84  },85  {86    code: "27-3091.00",87    title: "Interpreters and Translators",88    description: "Translate or interpret written and spoken material between languages.",89    wageCents: 5_768_000_00 / 100,90    profile: p(4, 4, 4, 2, 3, 4),91    tasks: [92      { id: "T1", statement: "Translate written documents while preserving meaning, tone and register.", importance: 4.6 },93      { id: "T2", statement: "Interpret spoken exchanges in real time between parties.", importance: 4.0, adjust: { barriers: 1 } },94      { id: "T3", statement: "Proofread and quality-check translated content for accuracy.", importance: 3.7, adjust: { augmentation: 1 } },95    ],96  },97  {98    code: "15-1252.00",99    title: "Software Developers",100    description: "Design, build and maintain software systems and applications.",101    wageCents: 13_047_000_00 / 100,102    profile: p(4, 4, 3, 2, 5, 5),103    tasks: [104      { id: "T1", statement: "Write, test and debug application code to specification.", importance: 4.7 },105      { id: "T2", statement: "Design system architecture and negotiate technical trade-offs with stakeholders.", importance: 4.2, adjust: { automatability: -2, barriers: 1 } },106      { id: "T3", statement: "Review code, write documentation and maintain existing services.", importance: 3.9 },107    ],108  },109  {110    code: "13-2051.00",111    title: "Financial and Investment Analysts",112    description: "Analyze financial data to guide investment and business decisions.",113    wageCents: 9_918_000_00 / 100,114    profile: p(4, 3, 3, 3, 4, 5),115    tasks: [116      { id: "T1", statement: "Gather and consolidate financial data into models and reports.", importance: 4.5, adjust: { automatability: 1 } },117      { id: "T2", statement: "Assess valuation and risk to produce investment recommendations.", importance: 4.3, adjust: { barriers: 1 } },118      { id: "T3", statement: "Present findings and defend assumptions to clients and committees.", importance: 3.8, adjust: { automatability: -2 } },119    ],120  },121  {122    code: "23-1011.00",123    title: "Lawyers",124    description: "Advise and represent clients in legal matters and transactions.",125    wageCents: 14_531_000_00 / 100,126    profile: p(3, 3, 3, 5, 3, 4),127    tasks: [128      { id: "T1", statement: "Research case law and draft contracts, briefs and legal memoranda.", importance: 4.5, adjust: { automatability: 1, augmentation: 1 } },129      { id: "T2", statement: "Advise clients on legal strategy, risk and obligations.", importance: 4.4 },130      { id: "T3", statement: "Represent clients in negotiations, hearings and court proceedings.", importance: 4.0, adjust: { automatability: -1 } },131    ],132  },133  {134    code: "25-2021.00",135    title: "Elementary School Teachers",136    description: "Teach academic and social skills to students at the elementary level.",137    wageCents: 6_355_000_00 / 100,138    profile: p(2, 2, 2, 4, 2, 4),139    tasks: [140      { id: "T1", statement: "Prepare lesson plans, materials and assessments.", importance: 4.4, adjust: { automatability: 1, augmentation: 1 } },141      { id: "T2", statement: "Instruct and manage a classroom of students in person.", importance: 4.8, adjust: { automatability: -1, barriers: 1 } },142      { id: "T3", statement: "Grade assignments and track individual student progress.", importance: 3.9, adjust: { automatability: 1 } },143    ],144  },145  {146    code: "29-1141.00",147    title: "Registered Nurses",148    description: "Provide and coordinate patient care and educate patients about health conditions.",149    wageCents: 8_607_000_00 / 100,150    profile: p(2, 2, 2, 5, 3, 3),151    tasks: [152      { id: "T1", statement: "Assess patients and administer treatments and medications.", importance: 4.8, adjust: { automatability: -1 } },153      { id: "T2", statement: "Document care and maintain accurate patient records.", importance: 4.2, adjust: { automatability: 2, augmentation: 1 } },154      { id: "T3", statement: "Educate patients and families on conditions and care plans.", importance: 4.0 },155    ],156  },157  {158    code: "53-3032.00",159    title: "Heavy and Tractor-Trailer Truck Drivers",160    description: "Drive heavy trucks to transport goods over intercity routes.",161    wageCents: 5_411_000_00 / 100,162    profile: p(2, 2, 2, 4, 2, 2),163    tasks: [164      { id: "T1", statement: "Drive long-haul routes while complying with hours-of-service rules.", importance: 4.8, adjust: { adoption_velocity: 1 } },165      { id: "T2", statement: "Inspect vehicles and secure cargo before and after trips.", importance: 4.1 },166      { id: "T3", statement: "Plan routes and log trip records and fuel purchases.", importance: 3.5, adjust: { automatability: 2, augmentation: 1 } },167    ],168  },169  {170    code: "47-2111.00",171    title: "Electricians",172    description: "Install, maintain and repair electrical wiring and systems.",173    wageCents: 6_121_000_00 / 100,174    profile: p(1, 1, 2, 4, 2, 2),175    tasks: [176      { id: "T1", statement: "Install and repair wiring, fixtures and control systems on site.", importance: 4.8 },177      { id: "T2", statement: "Diagnose faults using test equipment and building plans.", importance: 4.3, adjust: { augmentation: 1 } },178      { id: "T3", statement: "Prepare estimates, order materials and document completed work.", importance: 3.4, adjust: { automatability: 2, augmentation: 1 } },179    ],180  },181];182183const clamp = (value: number): number => Math.max(1, Math.min(5, Math.round(value)));184185async function seedDemoOccupation(occupation: DemoOccupation): Promise<void> {186  await prisma.occupation.upsert({187    where: { code: occupation.code },188    update: { title: occupation.title, description: occupation.description, medianWageCents: Math.round(occupation.wageCents) },189    create: {190      code: occupation.code,191      title: occupation.title,192      description: occupation.description,193      medianWageCents: Math.round(occupation.wageCents),194    },195  });196197  for (const [taskIndex, task] of occupation.tasks.entries()) {198    const taskId = `${occupation.code}-${task.id}`;199    await prisma.task.upsert({200      where: { id: taskId },201      update: { statement: task.statement, importance: task.importance },202      create: {203        id: taskId,204        occupationCode: occupation.code,205        statement: task.statement,206        importance: task.importance,207      },208    });209210    for (const [dimIndex, dimension] of DIMS.entries()) {211      const base = clamp(occupation.profile[dimension] + (task.adjust?.[dimension] ?? 0));212      // Deterministic panel disagreement: model B shifts by -1/0/+1 in a fixed pattern.213      const shift = ((dimIndex + taskIndex) % 3) - 1;214      const panel: Array<[string, number]> = [215        ["demo-panel-a", base],216        ["demo-panel-b", clamp(base + shift)],217      ];218      for (const [model, rating] of panel) {219        await prisma.taskRating.upsert({220          where: {221            taskId_dimension_model_promptVersion_sampleIndex: {222              taskId,223              dimension,224              model,225              promptVersion: "v1",226              sampleIndex: 0,227            },228          },229          update: { rating },230          create: {231            taskId,232            dimension,233            model,234            promptVersion: "v1",235            sampleIndex: 0,236            rating,237            rationale: "Demonstration rating (synthetic panel) — not an LLM output.",238            rawResponse: { demo: true },239          },240        });241      }242    }243  }244}245246async function main(): Promise<void> {247  for (const occupation of DEMO) {248    await seedDemoOccupation(occupation);249  }250  console.log(251    `Seeded ${DEMO.length} demo occupations (${DEMO.reduce((n, o) => n + o.tasks.length, 0)} tasks) with synthetic panel ratings.`,252  );253  console.log("Next: RATER_MODELS=demo-panel-a,demo-panel-b pnpm score:recompute");254}255256main()257  .catch((error) => {258    console.error(error);259    process.exitCode = 1;260  })261  .finally(() => prisma.$disconnect());262