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>
300 lines
13 KiB
Markdown
300 lines
13 KiB
Markdown
# 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.
|