# 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.