synops/docs/features/ressursforbruk.md
vegard d7dffa06e6 Fullfører oppgave 15.8: Forbruksoversikt i admin
Aggregert ressursforbruk-dashboard som spør mot resource_usage_log
(oppgave 15.7). Tre visninger: totaler per ressurstype, per samling,
og daglig tidsserie. AI drill-down viser forbruk per jobbtype og
modellnivå (fast/smart/deep).

Backend: GET /admin/usage med days- og collection_id-filtre.
Frontend: /admin/usage med filterbare tabeller og fargekodede kort.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 04:34:08 +00:00

238 lines
6.9 KiB
Markdown

# Ressursforbruk — Måling og synliggjøring
## Konsept
Alle ressurskrevende operasjoner logges med naturlige enheter.
Forbruket akkumuleres på tre akser: noden som ble behandlet,
brukeren som utløste det, og samlingen det skjedde i.
Formålet er synliggjøring og innsikt, ikke fakturering.
## Ressurstyper
| Ressurstype | Enhet | Hva måles |
|---|---|---|
| `ai` | tokens inn / tokens ut | LLM-kall via AI Gateway |
| `whisper` | sekunder prosessert lyd | Transkripsjons-pipeline |
| `tts` | tegn | Tekst-til-tale-generering |
| `cas` | bytes | Lagring i CAS (store/delete) |
| `bandwidth` | bytes ut | Servering av mediefiler og publisert innhold |
| `livekit` | deltaker-minutter | WebRTC-sesjoner (møter, opptak) |
| `graph` | noder / edges | Opprettelse av noder og edges i grafen |
## Logg-skjema
```sql
CREATE TABLE resource_usage_log (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
target_node_id UUID NOT NULL REFERENCES nodes(id),
triggered_by UUID REFERENCES nodes(id), -- null for system-jobber
collection_id UUID REFERENCES nodes(id),
resource_type TEXT NOT NULL, -- 'ai', 'whisper', 'tts', 'cas', 'bandwidth', 'livekit'
detail JSONB NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_resource_usage_target ON resource_usage_log(target_node_id);
CREATE INDEX idx_resource_usage_triggered ON resource_usage_log(triggered_by);
CREATE INDEX idx_resource_usage_collection ON resource_usage_log(collection_id);
CREATE INDEX idx_resource_usage_type_time ON resource_usage_log(resource_type, created_at);
```
## Detail-struktur per type
### AI (LLM-kall)
```jsonc
{
"model_level": "fast", // "fast" | "smart" | "deep"
"model_id": "gemini-2.0-flash",
"tokens_in": 1240,
"tokens_out": 380,
"job_type": "auto_tag" // hva jobben var
}
```
Modellnivåer:
| Nivå | Semantikk | Typiske modeller |
|---|---|---|
| `fast` | Billig, lav latens | Gemini Flash, Haiku |
| `smart` | Balansert | Sonnet, Gemini Pro |
| `deep` | Grundig, dyr | Opus, GPT-4 |
### Whisper (transkripsjon)
```jsonc
{
"model": "medium", // "small" | "medium" | "large-v3"
"duration_seconds": 2520, // lengde på prosessert lyd
"language": "no",
"mode": "batch" // "live" | "batch"
}
```
### TTS (tekst-til-tale)
```jsonc
{
"provider": "elevenlabs", // "elevenlabs" | "local"
"characters": 8200,
"voice_id": "norwegian_male_1"
}
```
### CAS (lagring)
```jsonc
{
"hash": "sha256-abc123...",
"size_bytes": 84000000,
"mime": "audio/mp3",
"operation": "store" // "store" | "delete"
}
```
### Bandwidth (servering)
```jsonc
{
"size_bytes": 84000000,
"path": "/media/podcast/ep47.mp3",
"client": "Apple Podcasts" // parsert fra User-Agent
}
```
### LiveKit (sanntid)
```jsonc
{
"room_id": "meeting-abc123",
"participant_minutes": 180,
"tracks": 4 // antall aktive lyd/video-spor
}
```
### Graph (noder og edges)
Trenger ikke logges i `resource_usage_log` — kan telles direkte fra
`nodes` og `edges`-tabellene med `COUNT` + `GROUP BY created_by` eller
`GROUP BY collection`. Billig spørring, ingen ekstra lagring.
Vises i bruker- og samlingsvisning som kontekst:
```
Vegard:
423 noder opprettet
1 204 edges
Sidelinja:
2 891 noder
8 340 edges
```
## Aggregering
Tre naturlige visninger, alle er GROUP BY-spørringer mot samme tabell:
### Per node
Synlig i node-detaljer for eieren. Gir innsikt i hva en spesifikk
node har kostet i ressurser.
```
Episode 47:
AI (smart) 12k tokens inn, 3k ut — 4 jobber
Whisper 42 min prosessert (medium)
TTS 8 200 tegn
CAS 84 MB lagret
Båndbredde 2.3 GB servert
LiveKit 180 deltaker-minutter
12 noder, 34 edges
```
### Per bruker
Synlig for brukeren selv i sin profil/innstillinger. Sum av alle
noder brukeren har utløst arbeid på.
```
Vegard denne måneden:
AI fast: 42k / smart: 18k / deep: 3k tokens
Whisper 3.2 timer prosessert
TTS 24k tegn
423 noder opprettet, 1 204 edges
```
### Per samling
Synlig for samlingens eiere/admins. Sum av alt forbruk i samlingen.
Nyttig for å forstå hvilke samlinger som bruker mest ressurser.
```
Sidelinja (mars 2026):
AI 148k tokens totalt
Whisper 12.4 timer prosessert
CAS 2.1 GB lagret
Båndbredde 48 GB servert
2 891 noder, 8 340 edges
```
## Triggered-by-regler
| Scenario | triggered_by |
|---|---|
| Bruker klikker "oppsummer" | Brukeren |
| Bruker sender melding som trigger auto-tag | Brukeren |
| Nattlig samlings-digest | null (system) |
| Podcast-nedlasting av ekstern lytter | null (system) |
Når `triggered_by` er null, tilhører forbruket kun samlingen —
det belaster ingen spesifikk bruker.
## Logging-ansvar
Maskinrommet logger all ressursbruk. Hver handler (AI, Whisper, TTS,
CAS, LiveKit) skriver til `resource_usage_log` som siste steg etter
vellykket operasjon. Feilede jobber logges ikke — ingen ressurs ble
forbrukt.
Båndbredde-logging skjer via Caddy-logg-parsing i nattlig batch-jobb
(samme mønster som `docs/features/podcast_statistikk.md`).
## Implementeringsstatus
Følgende ressurstyper logges til `resource_usage_log`:
| Ressurstype | Handler | Status |
|---|---|---|
| `ai` | `summarize.rs`, `ai_edges.rs`, `agent.rs` | Implementert. Token-telling fra LiteLLM `usage`-feltet. Agent bruker `claude` CLI og logger 0 tokens (CLI gir ikke token-info). |
| `whisper` | `transcribe.rs` | Implementert. Logger `duration_seconds`, `model`, `language`, `mode`. |
| `tts` | `tts.rs` | Implementert. Logger `provider`, `characters`, `voice_id`. |
| `cas` | `intentions.rs` (upload_media) | Implementert. Logger kun nye filer (ikke dedup). `hash`, `size_bytes`, `mime`, `operation`. |
| `livekit` | `intentions.rs` (join_communication) | Implementert. Logger `join`-hendelser. Faktisk `participant_minutes` krever LiveKit webhook-integrasjon (fremtidig). |
| `bandwidth` | `bandwidth.rs` | Implementert. Nattlig jobb (kl 03:00) parser Caddy JSON-access-logger. |
### Sentralisert hjelpemodul
`resource_usage.rs` tilbyr `log()` og `find_collection_for_node()`.
Alle handlers bruker denne for konsistent logging.
### Admin-dashboard (oppgave 15.8)
`/admin/usage` viser aggregert forbruksoversikt:
- **Totalkort** per ressurstype med naturlige enheter (tokens, timer, GB, tegn, minutter)
- **Per samling**-tabell: filtrerbar på ressurstype og tidsperiode (7/30/90/365 dager)
- **AI drill-down**: per jobbtype og modellnivå (fast/smart/deep), tokens inn/ut
- **Daglig tidsserie**: aktivitet per dag og ressurstype
- Samlings- og ressurstype-filtre med live-oppdatering
Backend: `maskinrommet/src/usage_overview.rs``GET /admin/usage?days=30&collection_id=<uuid>`
Frontend: `frontend/src/routes/admin/usage/+page.svelte`
### Caddy-oppsett
JSON access logging er konfigurert i Caddyfile for `sidelinja.org`
og `synops.no`. Logger skrives til `/var/log/caddy/access-*.log`
med 100 MiB rotasjon og 7 filer beholdt.