# Plan: wekelijkse sync van `model_prices` (PBI-66 / ST-1296) ## Context De tabel `model_prices` ([prisma/schema.prisma:465](../../prisma/schema.prisma)) bevat nu 3 hardcoded rijen via [prisma/seed.ts:198](../../prisma/seed.ts) (Opus 4.7, Sonnet 4.6, Haiku 4.5). Die wordt door [lib/insights/token-stats.ts](../../lib/insights/token-stats.ts) en [lib/insights/token-history.ts](../../lib/insights/token-history.ts) ge-`LEFT JOIN`-d voor kostberekening. Probleem: prijzen + nieuwe modellen worden alleen bijgewerkt bij een full re-seed. Dat vergeet je. We willen een wekelijks handmatig draaibaar script dat: 1. De actuele Claude 4.x modellijst ophaalt bij Anthropic (`GET /v1/models`), 2. Per model de prijzen bepaalt uit een onderhouden tabel in code, 3. Nieuwe modellen detecteert en logt (zodat we weten dat de tabel update nodig heeft), 4. Idempotent upsert in `model_prices`. **Belangrijke realiteit:** Anthropic biedt geen prijs-API. `/v1/models` levert id, display_name, max_tokens, capabilities — maar **geen pricing**. De prijzen onderhouden we daarom als constanten in het script. De API-call dient om de modellijst te valideren en nieuwe modellen op te merken. ## Aanpak Eén nieuw TypeScript-script `scripts/sync-model-prices.ts` in dezelfde stijl als [scripts/insert-milestone.ts](../../scripts/insert-milestone.ts): - dotenv → DATABASE_URL + ANTHROPIC_API_KEY - `pg.Pool` + `PrismaPg` adapter + `PrismaClient` (zelfde patroon als bestaande scripts) - `--dry-run` flag voor preview zonder schrijven - Aangeroepen via `npm run db:sync-model-prices` ### Datastromen ``` ANTHROPIC API /v1/models │ ▼ (filter: model_id matcht /^claude-(opus|sonnet|haiku)-4/) API model list ───────────┐ │ PRICE_TABLE (in script) ──┤── join op model_id │ ▼ Per model: - input_price = PRICE_TABLE[id].input - output_price = PRICE_TABLE[id].output - cache_read_price = input * 0.1 - cache_write_price = input * 1.25 │ ▼ prisma.modelPrice.upsert ``` ### PRICE_TABLE in script ```ts const PRICE_TABLE: Record = { 'claude-opus-4-7': { input: 15.0, output: 75.0 }, 'claude-sonnet-4-6': { input: 3.0, output: 15.0 }, 'claude-haiku-4-5-20251001': { input: 0.8, output: 4.0 }, } const CACHE_READ_RATIO = 0.1 const CACHE_WRITE_RATIO = 1.25 // 5-minute cache write ``` Cache-ratio's komen overeen met de huidige seed: 1.5/15 = 0.1 en 18.75/15 = 1.25 — dus geen waarde-shift voor bestaande rijen. ### Filter Claude 4.x Regex op `id` uit de API: `/^claude-(opus|sonnet|haiku)-4/`. Dit matcht `claude-opus-4-7`, `claude-sonnet-4-6`, `claude-haiku-4-5-20251001` en toekomstige 4.x varianten. Filtert oudere 3.x modellen weg. ### Detectie nieuwe modellen Per Claude 4.x model uit de API: - **In PRICE_TABLE** → upsert met de prijs - **Niet in PRICE_TABLE** → log warning, sla over, exit code blijft 0 ## Bestanden | Bestand | Actie | |---|---| | `scripts/sync-model-prices.ts` | **Nieuw** — sync-script | | `package.json` | **Wijzigen** — entry `"db:sync-model-prices"` toevoegen | | `.env.example` | **Wijzigen** — `ANTHROPIC_API_KEY=""` toevoegen | | `lib/env.ts` | **Wijzigen** — `ANTHROPIC_API_KEY` als optional env var | | `scripts/README.md` | **Wijzigen** — sectie "Sync model prices" toevoegen | | `prisma/seed.ts` regels 198–229 | **Behouden** — fallback voor verse DB | ## Edge cases | Geval | Gedrag | |---|---| | `ANTHROPIC_API_KEY` ontbreekt | Error + exit 1 | | API geeft 401 | Error met hint "controleer API key" | | API geeft 5xx | Retry 1× met 2s delay, dan falen | | API levert 0 Claude 4.x modellen | Warning, exit 1 | | Model uit DB staat niet meer in API | Niet verwijderen — alleen loggen | | `--dry-run` | API-call gewoon doen, alleen geen `upsert` | ## Verificatie ```bash npm run db:sync-model-prices -- --dry-run npm run db:sync-model-prices psql $DATABASE_URL -c "SELECT model_id, input_price_per_1m, output_price_per_1m, updated_at FROM model_prices ORDER BY model_id" npm run lint && npm test && npm run build ``` ## Buiten scope - Geen Vercel cron route — bewust gekozen: handmatig draaien geeft moment om PRICE_TABLE bij te werken. - Geen pricing-page scraping — fragiel. - Geen 1-uurs cache write tier — schema heeft één veld.