From 68d8fff2dde96fad0a2c91371c28d618e3e6b1bb Mon Sep 17 00:00:00 2001 From: vegard Date: Fri, 20 Mar 2026 01:21:44 +0000 Subject: [PATCH] Spec: brukerklasser og AI-budsjettering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Konfigurerbare brukerklasser (Basis, Proff(ish), Superduper ultra premium, Admin) med token-budsjett per dag, modellnivå-tilgang og feature-gates. Budsjettsjekk før hvert LLM-kall. Admin-forbruk vises med kostnadsestimat. Automatiske triggere teller mot brukerens budsjett. Klasser og brukere som noder i grafen. Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/infra/brukerklasser.md | 300 ++++++++++++++++++++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 docs/infra/brukerklasser.md diff --git a/docs/infra/brukerklasser.md b/docs/infra/brukerklasser.md new file mode 100644 index 0000000..055c1d2 --- /dev/null +++ b/docs/infra/brukerklasser.md @@ -0,0 +1,300 @@ +# Brukerklasser og AI-budsjettering + +## Konsept + +Brukerklasser styrer tilgang til AI-ressurser. Hver klasse +definerer token-budsjett, tilgjengelige modellnivåer og +hvilke AI-features brukeren kan trigge. Admin konfigurerer +klasser og tildeler brukere. + +## Brukerklasser som noder + +``` +node_kind: 'user_class' +title: "Proff(ish)" +metadata: { + budget_tokens_per_day: 500000, + model_access: ["synops/low", "synops/medium"], + features: ["chat_bot", "summarize", "suggest_edges", "ai_process"], + priority: 2, // høyere = finere (for sortering) + description: "For de som mener alvor. Noen ganger." +} +``` + +## Default-klasser + +| Klasse | Budsjett/dag | Modeller | Features | +|--------|-------------|----------|----------| +| **Basis** | 50k tokens | low | chat-bot | +| **Proff(ish)** | 500k tokens | low, medium | chat-bot, oppsummering, suggest-edges, ai-prosessering | +| **Superduper ultra premium** | 5M tokens | low, medium, high | alt unntatt modellvalg og konfig | +| **Admin** | ubegrenset | alle inkl. extreme | alt + modellvalg (/claude, /grok) + konfig | + +Klassenavnene er konfigurerbare. Default-navnene er selvironiske +i tråd med plattformens tone. Admin kan opprette, endre, slette +og omdøpe klasser fritt. + +## Tildeling + +Bruker → klasse via edge: + +``` +person_node --[has_class]--> user_class_node +``` + +Én klasse per bruker. Default for nye brukere: "Basis" (eller +konfigurerbar i admin). Admin kan endre klasse per bruker. + +## Budsjettsjekk + +Maskinrommet sjekker budsjett **før** hvert LLM-kall: + +```rust +async fn check_budget(user_node_id: Uuid, pool: &PgPool) -> Result { + // Hent brukerens klasse + let class = get_user_class(user_node_id, pool).await?; + + // Ubegrenset? Alltid OK. + if class.budget_tokens_per_day == 0 { return Ok(true); } + + // Hent forbruk siste 24 timer + let used = sqlx::query_scalar!( + "SELECT COALESCE(SUM(input_tokens + output_tokens), 0) + FROM ai_usage_log + WHERE user_node_id = $1 + AND created_at > now() - interval '1 day'", + user_node_id + ).fetch_one(pool).await?; + + Ok(used < class.budget_tokens_per_day) +} +``` + +Ved overskridelse: +- LLM-kall avvises +- Bruker får melding: "Daglig budsjett brukt opp. Tilbakestilles kl. 00:00." +- Admin varsles hvis ønskelig +- Jobber som trigger for brukeren køes til neste dag (eller droppes) + +## Modellnivå-gate + +I tillegg til token-budsjett: brukerens klasse bestemmer hvilke +modellnivåer (synops/low, medium, high, extreme) de kan bruke. + +```rust +async fn check_model_access(user_node_id: Uuid, model_alias: &str, pool: &PgPool) -> bool { + let class = get_user_class(user_node_id, pool).await.unwrap(); + class.model_access.contains(model_alias) +} +``` + +"Basis"-brukere får alltid `synops/low`. Prøver de en feature +som krever `synops/high`, downgrades modellen automatisk til +det høyeste nivået de har tilgang til — med varsel om at +resultatet kan være dårligere. + +## Automatiske triggere og budsjett + +Når en orkestrering (som suggest-edges) trigger på en brukers +handling, teller kostnadene mot **brukerens** budsjett: + +``` +Trond oppretter en innholds-node + → orkestrering: suggest-edges triggers + → budsjettsjekk: har Trond (Proff(ish)) tokens igjen? + → ja: kjør suggest-edges, logg mot Trond + → nei: dropp, logg "skipped:budget" +``` + +Systemjobber (RSS, rendering) som ikke er bruker-initiert +kjører under et "system"-budsjett som admin styrer separat. + +## Feature-gate + +Hver AI-feature har et navn brukt i feature-listen: + +| Feature-nøkkel | Beskrivelse | +|-----------------|------------| +| `chat_bot` | @bot i samtaler | +| `summarize` | AI-oppsummering | +| `suggest_edges` | Automatisk foreslåtte relasjoner | +| `ai_process` | AI-prosessering (verktøy-panel) | +| `transcribe` | Whisper-transkribering | +| `tts` | Tekst-til-tale | +| `clip` | Web clipper med AI-oppsummering | +| `orchestration` | AI-steg i orkestreringer | +| `model_select` | Eksplisitt modellvalg (/claude, /grok) | +| `admin_config` | Konfigurasjon av klasser og ruting | + +Klassen lister hvilke features den har tilgang til. Maskinrommet +sjekker feature-tilgang før det tillater operasjonen. + +## Admin-UI: Brukerpanel + +### Brukeroversikt (/admin/users) + +``` +┌─ Brukere ──────────────────────────────────────────┐ +│ │ +│ Navn Klasse Forbruk Status │ +│ Vegard Admin 1.2M i dag ✅ │ +│ Trond Superduper ultra.. 2.1M/5M ✅ │ +│ Arne Proff(ish) 430k/500k ⚠️ │ +│ Lise Basis 48k/50k ⚠️ │ +│ Gjest Basis 0/50k ✅ │ +│ │ +│ Klikk bruker → detaljer med klasse-endring │ +└─────────────────────────────────────────────────────┘ +``` + +### Brukerdetaljer + +``` +┌─ Arne ─────────────────────────────────────────────┐ +│ │ +│ Klasse: [Proff(ish) ▼] │ +│ │ +│ Forbruk i dag: 430,000 / 500,000 tokens (86%) │ +│ Forbruk denne uken: 1.8M tokens │ +│ ████████████████████░░░ │ +│ │ +│ Modellfordeling i dag: │ +│ synops/low: 380k tokens (88%) │ +│ synops/medium: 50k tokens (12%) │ +│ │ +│ Siste AI-kall: │ +│ 14:30 chat_bot (synops/low) 1.2k tokens │ +│ 14:15 suggest_edges (synops/low) 800 tokens │ +│ 13:50 summarize (synops/medium) 3.5k tokens │ +│ │ +│ [Overstyr budsjett] [Deaktiver bruker] │ +└─────────────────────────────────────────────────────┘ +``` + +### Admin-forbruk + +Admin har ubegrenset budsjett, men forbruket vises like fullt: + +``` +┌─ Vegard (Admin) ───────────────────────────────────┐ +│ │ +│ Forbruk i dag: 1,200,000 tokens │ +│ Forbruk denne uken: 8.3M tokens │ +│ Forbruk denne måneden: 31M tokens │ +│ │ +│ Estimert kostnad (basert på modellpriser): │ +│ synops/low: 800k × $0.10/M = $0.08 │ +│ synops/medium: 300k × $0.50/M = $0.15 │ +│ synops/high: 100k × $3.00/M = $0.30 │ +│ ───────────────────────────────── │ +│ Total i dag: ~$0.53 │ +│ Total denne uken: ~$3.70 │ +│ Total denne måneden: ~$14.20 │ +│ │ +│ Claude Code (abonnement, ikke tokens): │ +│ Sesjoner i dag: 3 │ +│ Total tid: 4t 20min │ +└─────────────────────────────────────────────────────┘ +``` + +### Klasse-konfigurasjon (/admin/classes) + +``` +┌─ Brukerklasser ────────────────────────────────────┐ +│ │ +│ Navn Budsjett Modeller │ +│ Basis 50k/dag low │ +│ Proff(ish) 500k/dag low+medium │ +│ Superduper ultra premium 5M/dag low+med+high │ +│ Admin ∞ alle │ +│ │ +│ [+ Ny klasse] │ +└─────────────────────────────────────────────────────┘ + +Klikk en klasse → rediger: + +┌─ Rediger: Proff(ish) ─────────────────────────────┐ +│ │ +│ Navn: [Proff(ish) ] │ +│ Beskrivelse: [For de som mener alvor. ] │ +│ │ +│ Budsjett: [500000] tokens per [dag ▼] │ +│ │ +│ Modellnivåer: │ +│ ☑ synops/low │ +│ ☑ synops/medium │ +│ ☐ synops/high │ +│ ☐ synops/extreme │ +│ │ +│ Features: │ +│ ☑ chat_bot ☑ summarize │ +│ ☑ suggest_edges ☑ ai_process │ +│ ☐ transcribe ☐ tts │ +│ ☐ model_select ☐ admin_config │ +│ │ +│ Brukere i denne klassen: 2 (Arne, Lise) │ +│ │ +│ [Lagre] [Avbryt] │ +└─────────────────────────────────────────────────────┘ +``` + +## Systembudsjett + +Jobber som ikke tilhører en spesifikk bruker (RSS-generering, +planlagt publisering, system-orkestreringer) kjører under et +eget systembudsjett: + +``` +metadata på system-config-node: { + system_budget_tokens_per_day: 1000000, + system_model_level: "synops/low" +} +``` + +Vises i admin-panelet under "Systemforbruk". + +## Implementering + +### Database + +Ingen ny tabell — brukerklasser er noder: + +```sql +-- Opprett default-klasser (seed) +INSERT INTO nodes (node_kind, title, visibility, metadata) VALUES +('user_class', 'Basis', 'readable', '{"budget_tokens_per_day":50000,"model_access":["synops/low"],"features":["chat_bot"],"priority":1,"description":"Grunnpakken. Du får en bot. Vær takknemlig."}'), +('user_class', 'Proff(ish)', 'readable', '{"budget_tokens_per_day":500000,"model_access":["synops/low","synops/medium"],"features":["chat_bot","summarize","suggest_edges","ai_process"],"priority":2,"description":"For de som mener alvor. Noen ganger."}'), +('user_class', 'Superduper ultra premium', 'readable', '{"budget_tokens_per_day":5000000,"model_access":["synops/low","synops/medium","synops/high"],"features":["chat_bot","summarize","suggest_edges","ai_process","transcribe","tts","clip","orchestration"],"priority":3,"description":"Alt du kan drømme om. Nesten."}'), +('user_class', 'Admin', 'readable', '{"budget_tokens_per_day":0,"model_access":["synops/low","synops/medium","synops/high","synops/extreme"],"features":["chat_bot","summarize","suggest_edges","ai_process","transcribe","tts","clip","orchestration","model_select","admin_config"],"priority":99,"description":"Hersker over alt. Betaler regningen."}'); +``` + +(`budget_tokens_per_day: 0` = ubegrenset for Admin) + +### Maskinrommet + +- `check_ai_budget(user_node_id, estimated_tokens)` — kall før LLM +- `check_model_access(user_node_id, model_alias)` — kall før LLM +- `check_feature_access(user_node_id, feature_key)` — kall før feature +- Alle bruker `get_user_class()` som cacher i minne (invalideres ved endring) + +### Frontend + +- `/admin/users` — utvidet med klasse, forbruk, statusindikator +- `/admin/classes` — ny side for klasse-konfigurasjon +- Brukerens egen profil viser forbruk og gjenstående budsjett + +### Modellpris-tabell + +For å estimere kostnad i admin-UI: + +```sql +CREATE TABLE model_pricing ( + model_alias TEXT PRIMARY KEY, + input_price_per_m NUMERIC, -- pris per million input-tokens + output_price_per_m NUMERIC, -- pris per million output-tokens + updated_at TIMESTAMPTZ DEFAULT now() +); +``` + +Admin oppdaterer priser manuelt (de endres sjelden). +Brukes kun for visning — ikke for faktisk fakturering.