Docs: oppdater CLAUDE.md arbeidsflyt, ai_gateway og chat-docs
- CLAUDE.md: lagt til standard arbeidsmodus, testmiljø og browser-testing - ai_gateway.md: PG-eier-config arkitektur, datamodell, config-generering, admin-panel - chat.md: trådvisning, meldingskollaps, AI-behandling, konvertering Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
88a22e131b
commit
89a8f99766
3 changed files with 116 additions and 61 deletions
|
|
@ -5,6 +5,9 @@ Sidelinja er et redaksjonelt operativsystem og kunnskapsgraf for podcast-produks
|
|||
Self-hosted på Hetzner VPS med full datakontroll.
|
||||
|
||||
## Arbeidsflyt
|
||||
- **Standard arbeidsmodus:** Start i planleggingsmodus. Lag en grundig plan, få godkjenning, deretter implementer. Jobbene er ment å kunne kjøre lenge autonomt uten input underveis.
|
||||
- **Testmiljø:** `./dev.sh` er den kanoniske måten å starte utviklingsmiljøet. Når nye tjenester, steg eller oppsett-quirks oppdages, oppdater alltid `dev.sh` slik at kunnskapen bevares i scriptet — ikke bare i hodet. Før Vegard tester i browser: kjør `./dev.sh`, verifiser med `cargo check`/`svelte-check`/`curl`, og meld tilbake at det er klart.
|
||||
- **Browser-testing:** Claude har ikke tilgang til browser. Visuell testing og interaksjon gjøres av Vegard. Claude kan verifisere backend (kompilering, API-kall, database-state) men ikke frontend-rendering.
|
||||
- **Commit og push:** Bruk egen vurdering. Commit når arbeidet er logisk komplett, push til Forgejo når det gir mening. Ingen grunn til å spørre — det er trygt og reverserbart.
|
||||
- **Deploy til produksjon:** Krever alltid eksplisitt godkjenning fra Vegard. Deploy = SSH til server + pull + docker compose up. Aldri gjør dette uten å spørre først.
|
||||
- **Diskusjon:** Forklar og diskuter før arkitekturendringer eller uvanlige valg. For implementering innenfor eksisterende spec — bare kjør.
|
||||
|
|
|
|||
|
|
@ -86,13 +86,16 @@ Kun aktive når `config.mentions = true`.
|
|||
* **Mobil-optimalisert:** Autocomplete-listen er tappbar og tilpasset mindre skjermer.
|
||||
|
||||
## 5. Tråder
|
||||
Kun aktive når `config.threads = true`. Meldinger kan ha en `reply_to`-referanse. Frontend viser tråder som innrykk eller ekspanderbare grupper.
|
||||
Kun aktive når `config.threads = true`. Meldinger kan ha en `reply_to`-referanse. Frontend grupperer meldinger i tråder (rot + svar) med visuell skillelinje mellom hver tråd. Svar vises med innrykk og vertikal linje under rot-meldingen, uten ekstra skillelinje mellom rot og svar.
|
||||
|
||||
## 6. Vedlegg
|
||||
Kun aktive når `config.attachments = true`. Meldinger kan ha vedlegg via `message_attachments` → `media_files`. Whiteboard-eksport kan knyttes som vedlegg.
|
||||
|
||||
## 7. Versjonshistorikk
|
||||
Alle meldinger støtter redigering med full historikk via `message_revisions`. Original tekst bevares alltid.
|
||||
Alle meldinger støtter redigering med full historikk via `message_revisions`. Original tekst bevares alltid. AI-behandlede meldinger har en revisjons-toggle i UI — brukeren kan veksle mellom AI-versjon og original tekst. AI-output rendres som Markdown via `marked`.
|
||||
|
||||
## 7.1 Meldingsvisning
|
||||
Lange meldinger (mer enn 2 linjer) kollapses automatisk med en "Vis mer"-knapp. Ved ekspandering vises "Vis mindre" både over og under meldingen, slik at man slipper å scrolle for å kollapse igjen.
|
||||
|
||||
## 8. Tale-til-tekst (Voice-to-text)
|
||||
Mobilvennlig diktering for situasjoner der tastatur er upraktisk. Brukeren trykker en mikrofon-knapp, snakker, og får teksten tilbake som en vanlig melding klar til redigering og sending.
|
||||
|
|
@ -124,8 +127,11 @@ Channels med `config.ttl_days` satt til et tall får sine meldinger automatisk s
|
|||
- **Worker warmup (`worker/src/warmup.rs`):** PG → SpacetimeDB ved oppstart. Per-kanal konfig (all/messages/days/none). Trådbasert henting.
|
||||
- **Worker sync (`worker/src/sync.rs`):** SpacetimeDB → PG hvert sekund. Insert/delete/update meldinger + reaksjoner.
|
||||
- **Admin-side (`/admin/channels`):** Per-kanal warmup-konfigurasjon.
|
||||
- **Tråder:** Komplett trådvisning med datogruppering og autoscroll.
|
||||
- **Tråder:** Komplett trådvisning med datogruppering, autoscroll og visuell skillelinje mellom tråder.
|
||||
- **Reaksjoner:** Via SpacetimeDB-reducers, synket til PG.
|
||||
- **Meldingskollaps:** Lange meldinger begrenses til 2 linjer med "Vis mer"/"Vis mindre".
|
||||
- **AI-behandling:** Meldinger kan AI-behandles (✨-knapp). Revisjons-toggle viser original vs. AI-versjon. Markdown-rendering for AI-output.
|
||||
- **Konvertering:** Meldinger kan opprettes som kanban-kort eller kalenderhendelse (dialog sier "Opprett", ikke "Konverter" — meldingen beholdes i chatten).
|
||||
|
||||
### Gjenstår
|
||||
- **Vedlegg, TTL** — avventer implementering.
|
||||
|
|
|
|||
|
|
@ -24,59 +24,86 @@ Fordeler:
|
|||
|
||||
## 3. Modellruting
|
||||
|
||||
Modellvalg styres av to mekanismer:
|
||||
### 3.1 Arkitekturprinsipp: PG eier config, LiteLLM er stateløs
|
||||
|
||||
### 3.1 Standard ruting (config.yaml)
|
||||
LiteLLM konfigureres med modellaliaser som mapper til billigste egnede leverandør:
|
||||
PostgreSQL er single source of truth for all modellkonfigurasjon. LiteLLM er en stateløs proxy som får generert `config.yaml` fra PG-data. Dette gir:
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
# Ruting: billigste først, fallback til dyrere
|
||||
- model_name: "sidelinja/rutine"
|
||||
litellm_params:
|
||||
model: "gemini/gemini-2.0-flash"
|
||||
api_key: "os.environ/GEMINI_API_KEY"
|
||||
- model_name: "sidelinja/rutine"
|
||||
litellm_params:
|
||||
model: "openrouter/google/gemini-2.0-flash-001"
|
||||
api_key: "os.environ/OPENROUTER_API_KEY"
|
||||
* **Ingen avhengighet til LiteLLM sitt admin API** — de endrer API mellom versjoner
|
||||
* **All konfig i samme backup/migrasjon** som resten av systemet
|
||||
* **Enkel bytte** — hvis LiteLLM erstattes, er all konfig intakt i PG
|
||||
* **Admin-UI i SvelteKit** — gjenbruker eksisterende `/admin/`-mønster
|
||||
|
||||
- model_name: "sidelinja/resonering"
|
||||
litellm_params:
|
||||
model: "anthropic/claude-sonnet-4-20250514"
|
||||
api_key: "os.environ/ANTHROPIC_API_KEY"
|
||||
- model_name: "sidelinja/resonering"
|
||||
litellm_params:
|
||||
model: "openrouter/anthropic/claude-sonnet-4-20250514"
|
||||
api_key: "os.environ/OPENROUTER_API_KEY"
|
||||
### 3.2 Datamodell
|
||||
|
||||
router_settings:
|
||||
routing_strategy: "simple-shuffle" # prøv første, fallback til neste
|
||||
num_retries: 2
|
||||
timeout: 60
|
||||
```sql
|
||||
-- Globale modellaliaser (server-nivå, ikke per workspace)
|
||||
CREATE TABLE ai_model_aliases (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
alias TEXT NOT NULL, -- 'sidelinja/rutine', 'sidelinja/resonering'
|
||||
description TEXT, -- 'Billig, høyt volum'
|
||||
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
UNIQUE(alias)
|
||||
);
|
||||
|
||||
general_settings:
|
||||
master_key: "os.environ/LITELLM_MASTER_KEY"
|
||||
-- Leverandør-modeller med prioritert fallback per alias
|
||||
CREATE TABLE ai_model_providers (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
alias_id UUID NOT NULL REFERENCES ai_model_aliases(id) ON DELETE CASCADE,
|
||||
provider TEXT NOT NULL, -- 'gemini', 'openrouter', 'anthropic'
|
||||
model TEXT NOT NULL, -- 'gemini/gemini-2.5-flash', 'openrouter/anthropic/claude-sonnet-4'
|
||||
api_key_env TEXT NOT NULL, -- 'GEMINI_API_KEY', 'OPENROUTER_API_KEY'
|
||||
priority SMALLINT NOT NULL, -- lavere = prøves først
|
||||
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||
UNIQUE(alias_id, model)
|
||||
);
|
||||
|
||||
-- Jobbtype → modellalias mapping
|
||||
CREATE TABLE ai_job_routing (
|
||||
job_type TEXT PRIMARY KEY, -- 'ai_text_process', 'whisper_postprocess', etc.
|
||||
alias TEXT NOT NULL, -- 'sidelinja/rutine'
|
||||
description TEXT
|
||||
);
|
||||
```
|
||||
|
||||
### 3.2 Jobbkø-styrt modellvalg
|
||||
Jobbkøen (se `jobbkø.md`) spesifiserer modellalias per jobbtype:
|
||||
### 3.3 Config-generering
|
||||
|
||||
| Jobbtype | Modellalias | Begrunnelse |
|
||||
SvelteKit-serveren genererer `config.yaml` fra PG ved oppstart og ved endringer i admin-panelet:
|
||||
|
||||
1. Les aktive aliaser og deres providers (sortert etter priority)
|
||||
2. Skriv `config.yaml` til volum delt med LiteLLM-containeren
|
||||
3. Restart LiteLLM (`docker restart ai-gateway`) eller send `SIGHUP`
|
||||
|
||||
Generert config inkluderer alltid `router_settings` og `general_settings` fra faste verdier — kun `model_list` er dynamisk.
|
||||
|
||||
### 3.4 Jobbkø-styrt modellvalg
|
||||
|
||||
Jobbkøen bruker `ai_job_routing` for å bestemme modellalias per jobbtype:
|
||||
|
||||
| Jobbtype | Standard alias | Begrunnelse |
|
||||
|---|---|---|
|
||||
| `whisper_postprocess` (transkripsjonsvasking) | `sidelinja/rutine` | Høyt volum, lav kompleksitet |
|
||||
| `openrouter_analyze` (metadata-uttrekk) | `sidelinja/rutine` | Strukturert output, lav kompleksitet |
|
||||
| `research_clip` (research-oppsummering) | `sidelinja/rutine` | Høyt volum |
|
||||
| `live_factoid_eval` (live-assistent) | `sidelinja/resonering` | Krever presis vurdering under tidspress |
|
||||
| `ai_text_process` (✨-behandling) | `sidelinja/rutine` | Tekstvasking, høyt volum |
|
||||
| `whisper_postprocess` | `sidelinja/rutine` | Transkripsjonsvasking, høyt volum |
|
||||
| `research_clip` | `sidelinja/rutine` | Research-oppsummering, høyt volum |
|
||||
| `live_factoid_eval` | `sidelinja/resonering` | Krever presis vurdering under tidspress |
|
||||
|
||||
Modellalias lagres som felt på jobben i PG — kan overstyres manuelt per jobb ved behov.
|
||||
|
||||
### 3.5 Admin-panel (`/admin/ai`)
|
||||
|
||||
Admin-panelet lar administrator:
|
||||
* Se og redigere modellaliaser og deres fallback-liste (drag-and-drop prioritering)
|
||||
* Aktivere/deaktivere individuelle leverandør-modeller
|
||||
* Endre jobbtype → alias mapping
|
||||
* Se live-status: hvilke leverandører som svarer, responstider
|
||||
* Trigge config-regenerering og LiteLLM-restart
|
||||
|
||||
## 4. Docker-oppsett
|
||||
|
||||
```yaml
|
||||
# docker-compose.dev.yml / docker-compose.yml
|
||||
ai-gateway:
|
||||
image: ghcr.io/berriai/litellm:main
|
||||
image: ghcr.io/berriai/litellm:main-stable
|
||||
restart: unless-stopped
|
||||
command: --config /etc/litellm/config.yaml
|
||||
environment:
|
||||
|
|
@ -151,36 +178,55 @@ tests/prompts/
|
|||
└── dataset.json
|
||||
```
|
||||
|
||||
## 6. Kostnadskontroll
|
||||
## 6. Tokenregnskap og kostnadskontroll
|
||||
|
||||
LiteLLM har innebygd logging, men mangler workspace-nivå budsjettering. For å forhindre kostnadssprekk:
|
||||
### 6.1 Token-logging per workspace
|
||||
|
||||
### 6.1 Workspace-budsjett
|
||||
Hver workspace har et månedlig AI-budsjett lagret i `workspaces.settings` (JSONB):
|
||||
Rust-workeren logger tokenforbruk etter hvert AI-kall. Dataen lagres i PG:
|
||||
|
||||
```json
|
||||
{
|
||||
"ai_budget": {
|
||||
"monthly_limit_usd": 50,
|
||||
"alert_threshold_pct": 80,
|
||||
"auto_fallback": true
|
||||
}
|
||||
}
|
||||
```sql
|
||||
CREATE TABLE ai_usage_log (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
workspace_id UUID NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
|
||||
job_id UUID REFERENCES job_queue(id) ON DELETE SET NULL,
|
||||
model_alias TEXT NOT NULL, -- 'sidelinja/rutine'
|
||||
model_actual TEXT, -- 'gemini/gemini-2.5-flash' (fra LiteLLM-respons)
|
||||
prompt_tokens INT NOT NULL,
|
||||
completion_tokens INT NOT NULL,
|
||||
total_tokens INT NOT NULL,
|
||||
estimated_cost NUMERIC(10, 6), -- USD, beregnet fra kjente priser
|
||||
job_type TEXT, -- 'ai_text_process', etc.
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_ai_usage_workspace_month ON ai_usage_log (workspace_id, created_at);
|
||||
```
|
||||
|
||||
- **Sporing:** SvelteKit logger token-bruk per AI-kall med workspace_id og jobbtype i `ai_usage_log`-tabellen (flyktig, TTL 90 dager).
|
||||
- **Alert:** Når 80 % av budsjettet er brukt, postes varsel i workspace-chat (system-channel).
|
||||
- **Auto-fallback:** Når budsjettet er nådd og `auto_fallback: true`, rutes alle kall til `sidelinja/rutine` (billigste modell). Ellers blokkeres AI-kall med feilmelding.
|
||||
**Flyten:**
|
||||
1. Rust-worker sender AI-kall via gateway, får tilbake `usage` i responsen
|
||||
2. Worker skriver rad til `ai_usage_log` med workspace_id, tokens og modellinfo
|
||||
3. Estimert kostnad beregnes fra en enkel prisliste i config (oppdateres manuelt)
|
||||
|
||||
### 6.2 Per-episode maks-kostnad
|
||||
### 6.2 Visning — to nivåer
|
||||
|
||||
**Admin (`/admin/ai`):**
|
||||
Aggregert oversikt over alle workspaces. Tabell med totaler per workspace/modell/periode. Identifiserer kostnadsdrivere.
|
||||
|
||||
**Workspace (sidebar-widget):**
|
||||
Enkel tekst-indikator i workspace-sidebar: `✨ 12.4k tokens denne uken`. Klikk åpner detaljert visning med fordeling per jobbtype og modell. Ingen speedometer — det krever et definert budsjett for å gi mening, og det er overkill for MVP.
|
||||
|
||||
### 6.3 Workspace-budsjett (fase 2)
|
||||
|
||||
Når token-logging er på plass, kan budsjett-tak legges til:
|
||||
|
||||
- Budsjett lagres i `workspaces.settings` (JSONB): `{ "ai_budget": { "monthly_limit_usd": 50 } }`
|
||||
- Rust-worker sjekker aggregert forbruk før AI-kall
|
||||
- Ved budsjett nær: fall tilbake til `sidelinja/rutine` (billigste)
|
||||
- Ved budsjett nådd: sett jobb i `paused` med varsel i workspace-chat
|
||||
|
||||
### 6.4 Per-episode maks-kostnad
|
||||
Podcastfabrikken-jobber (whisper + metadata + oppsummering) kan estimere totalkostnad basert på lydlengde. Jobben avbrytes med varsel hvis estimert kostnad overstiger `max_cost_per_episode` (default: $5).
|
||||
|
||||
### 6.3 Modell-nedgradering
|
||||
Jobbkøen støtter automatisk modell-nedgradering ved kostnadsmål:
|
||||
1. Prøv `sidelinja/resonering` (Claude)
|
||||
2. Ved budsjett-nær: fall tilbake til `sidelinja/rutine` (Gemini gratis)
|
||||
3. Ved budsjett-nådd: sett jobb i `paused`-status med varsel
|
||||
|
||||
## 7. Dataklassifisering (ref. docs/arkitektur.md 2.2)
|
||||
|
||||
| Data | Kategori | Detaljer |
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue