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>
This commit is contained in:
parent
9bc8624592
commit
68d8fff2dd
1 changed files with 300 additions and 0 deletions
300
docs/infra/brukerklasser.md
Normal file
300
docs/infra/brukerklasser.md
Normal file
|
|
@ -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<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.
|
||||||
Loading…
Add table
Reference in a new issue