synops/docs/infra/brukerklasser.md
vegard 68d8fff2dd Spec: brukerklasser og AI-budsjettering
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) <noreply@anthropic.com>
2026-03-20 01:21:44 +00:00

300 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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<bool> {
// 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.