diff --git a/.env.example b/.env.example index 6ef1f67..9d934b5 100644 --- a/.env.example +++ b/.env.example @@ -25,5 +25,9 @@ AUTH_SECRET= # openssl rand -base64 33 # URL til SpacetimeDB-instansen (server eller lokal) VITE_SPACETIMEDB_URL=ws://sidelinja.org/spacetime +# === Whisper (STT) === +# URL til faster-whisper-server (Docker-internt) +# WHISPER_URL=http://faster-whisper:8000 + # === Database (kun ved lokal PG, ellers via server) === # DATABASE_URL=postgres://sidelinja:localdev@localhost:5432/sidelinja diff --git a/docs/erfaringer/README.md b/docs/erfaringer/README.md index 8d3a8d2..3209294 100644 --- a/docs/erfaringer/README.md +++ b/docs/erfaringer/README.md @@ -13,6 +13,7 @@ Formålet er å treffe raskere blink med neste komponent. Hver fil dekker én te | `adapter_moenster.md` | Adapter/factory for PG↔SpacetimeDB, hybrid-tilnærming | | `authentik_oidc.md` | Authentik sub-claim format, @auth/sveltekit JWT-quirks | | `authentik_oppsett.md` | OIDC-provider/app-konfigurasjon, endepunkter, redirect URIs, API-admin | +| `faster_whisper_oppsett.md` | Docker-oppsett, CPU vs GPU, modellvalg, API-bruk, healthcheck-quirks | ## Retningslinjer diff --git a/docs/erfaringer/faster_whisper_oppsett.md b/docs/erfaringer/faster_whisper_oppsett.md new file mode 100644 index 0000000..10f31fe --- /dev/null +++ b/docs/erfaringer/faster_whisper_oppsett.md @@ -0,0 +1,111 @@ +# Erfaring: faster-whisper oppsett +**Dato:** 2026-03-17 + +## Kontekst +Oppgave 7.1 — sette opp faster-whisper som Docker-tjeneste for norsk tale-til-tekst (STT). +Serveren er en Hetzner VPS (8 vCPU, 16 GB RAM) uten GPU. + +## Valg + +### Image: `fedirz/faster-whisper-server` +- Gir OpenAI-kompatibelt HTTP API (`/v1/audio/transcriptions`) +- CPU-variant: `fedirz/faster-whisper-server:latest-cpu` +- GPU-variant: `fedirz/faster-whisper-server:latest-cuda` (for fremtidig bruk) +- Laster modell automatisk fra HuggingFace ved første request (lazy loading) + +### Modell: `large-v3` +- Best norsk kvalitet blant standard Whisper-modeller +- ~2.5 GB RAM med int8-kvantisering (CPU) +- Treigere enn `medium` på CPU, men akseptabelt for bakgrunnsjobber +- Alternativ: `NbAiLab/nb-whisper-large` (norsk-finjustert) — ikke testet + +### Kvantisering: `int8` +- Halverer RAM-bruk vs float32, minimal kvalitetstap +- Obligatorisk for CPU — float16 krever GPU + +## Docker Compose-konfigurasjon + +```yaml +faster-whisper: + image: fedirz/faster-whisper-server:latest-cpu + restart: unless-stopped + environment: + WHISPER__MODEL: large-v3 + WHISPER__INFERENCE_DEVICE: cpu + WHISPER__COMPUTE_TYPE: int8 + volumes: + - /srv/synops/data/whisper-models:/root/.cache/huggingface + - /srv/synops/media/cas:/srv/synops/media/cas:ro + networks: + - sidelinja-net + healthcheck: + test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen(\"http://localhost:8000/health\")"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 120s +``` + +## API-bruk + +Endepunkt fra Docker-nettverket: `http://faster-whisper:8000` + +```bash +# Enkel transkripsjon +curl -X POST http://faster-whisper:8000/v1/audio/transcriptions \ + -F "file=@lydfil.wav" \ + -F "model=large-v3" \ + -F "language=no" \ + -F "response_format=verbose_json" +``` + +Respons (verbose_json): +```json +{ + "task": "transcribe", + "language": "no", + "duration": 1.0, + "text": "...", + "segments": [ + { + "id": 1, + "start": 0.0, + "end": 3.5, + "text": "...", + "no_speech_prob": 0.01 + } + ] +} +``` + +## Ressursbruk +- **RAM:** ~2.5 GB (idle, modell lastet) +- **CPU:** Minimal ved idle. Bruker alle tilgjengelige kjerner under transkripsjon. +- **Disk:** ~3 GB for large-v3 modell (i `/srv/synops/data/whisper-models/`) + +## Gotchas +1. **Healthcheck:** Containeren har ikke `curl` installert. Bruk python3 urllib i stedet. +2. **Modellnedlasting:** Første request etter oppstart trigger modellnedlasting (~3 GB). + `start_period: 120s` i healthcheck gir tid til dette. +3. **CAS-tilgang:** Containeren monterer CAS read-only for direkte filtilgang. + Alternativt kan maskinrommet sende filer via multipart upload. +4. **Hallusinering:** Whisper hallusinerer tekst på stille/tone-filer (høy `no_speech_prob`). + Filtrér segmenter med `no_speech_prob > 0.6` i postprosessering. + +## Ved GPU-oppgradering +Bytt til CUDA-image og float16: +```yaml +faster-whisper: + image: fedirz/faster-whisper-server:latest-cuda + environment: + WHISPER__MODEL: large-v3 + WHISPER__INFERENCE_DEVICE: cuda + WHISPER__COMPUTE_TYPE: float16 + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: 1 + capabilities: [gpu] +``` diff --git a/docs/setup/produksjon.md b/docs/setup/produksjon.md index fa317dc..2a5b30d 100644 --- a/docs/setup/produksjon.md +++ b/docs/setup/produksjon.md @@ -7,6 +7,7 @@ Denne oppskriften tar en fersk Ubuntu VPS fra null til en komplett Synops-instal - Hetzner VPS med Ubuntu 24.04 LTS (8 vCPU, 16 GB RAM minimum) - DNS A-records som peker til VPS-ens IP: - `sidelinja.org` + `*.sidelinja.org` + - `synops.no` + `*.synops.no` - `vegard.info` + `*.vegard.info` - SSH-tilgang med nøkkelpar (passordautentisering deaktiveres i steg 1) @@ -61,6 +62,7 @@ newgrp docker sudo mkdir -p /srv/synops/{config,data,media,logs} sudo mkdir -p /srv/synops/config/{caddy,authentik} sudo mkdir -p /srv/synops/data/{postgres,spacetimedb,forgejo,authentik} +sudo mkdir -p /srv/synops/data/whisper-models sudo mkdir -p /srv/synops/media/podcast sudo mkdir -p /srv/synops/logs/caddy sudo chown -R sidelinja:sidelinja /srv/synops @@ -78,6 +80,7 @@ Resultat: │ ├── postgres/ │ ├── spacetimedb/ │ ├── forgejo/ +│ ├── whisper-models/ │ └── authentik/ ├── media/ │ └── podcast/ @@ -128,6 +131,10 @@ SPACETIMEDB_URL=http://spacetimedb:3000 SPACETIMEDB_DATABASE=synops SPACETIMEDB_TOKEN= +# === Whisper (STT) === +# Modell lastes ned automatisk ved oppstart. large-v3 gir best norsk kvalitet. +# Ved GPU: bytt image til fedirz/faster-whisper-server:latest-cuda og WHISPER__COMPUTE_TYPE=float16 + # === Intern === # Ingen porter eksponeres utenom 80/443. Alt rutes internt via Docker-nettverket. EOF @@ -182,6 +189,7 @@ services: maskinrommet: # Rust/axum API, intern port 3100, proxyet via Caddy livekit: # Intern port, proxyet via Caddy sveltekit: # Intern port, proxyet via Caddy + faster-whisper: # STT via OpenAI-kompatibelt API, intern port 8000 workers: # Rust job workers, ingen porter ``` @@ -225,6 +233,13 @@ git.sidelinja.org { reverse_proxy forgejo:3000 } +# === Synops (plattformdomene) === +# Subdomener (api.synops.no, auth.synops.no osv.) legges til individuelt +# etter behov — HTTP-challenge fungerer per subdomain uten DNS-plugin. +synops.no { + respond "synops.no — plattform under utvikling" 200 +} + # === Vegard.info === vegard.info { respond "vegard.info — under construction" 200 @@ -454,6 +469,7 @@ fra PG inn i STDB (warmup). Caddy proxyer `api.sidelinja.org` til port 3100. - [x] `https://sidelinja.org/api/health` returnerer 200 - [x] Authentik OIDC-innlogging fungerer fra nettleser (verifisert 2025-03-15) - [x] Chat: meldinger sendes og vises med riktig brukernavn (verifisert 2025-03-15) +- [ ] `https://synops.no` viser placeholder - [ ] `https://vegard.info` svarer - [ ] SpacetimeDB: WebSocket-tilkobling fra nettleser fungerer - [ ] LiveKit: Test-rom med video/lyd fungerer diff --git a/tasks.md b/tasks.md index aa19689..5ba5a94 100644 --- a/tasks.md +++ b/tasks.md @@ -93,8 +93,7 @@ Uavhengige faser kan fortsatt plukkes. ## Fase 7: Lyd-pipeline -- [~] 7.1 faster-whisper oppsett: Docker-container, GPU hvis tilgjengelig, norsk modell. Ref: `docs/erfaringer/`. - > Påbegynt: 2026-03-17T17:13 +- [x] 7.1 faster-whisper oppsett: Docker-container, GPU hvis tilgjengelig, norsk modell. Ref: `docs/erfaringer/`. - [ ] 7.2 Transkripsjons-pipeline: lydfil i CAS → maskinrommet trigger Whisper → resultat i `content`-feltet. - [ ] 7.3 Voice memo i frontend: opptak-knapp i input-komponenten → upload → CAS → transkripsjon. - [ ] 7.4 Lyd-avspilling: spiller av original lyd fra CAS-node. Waveform-visning. @@ -137,7 +136,7 @@ Uavhengige faser kan fortsatt plukkes. - [ ] 14.1 Tera-templates: innebygde temaer (avis, magasin, blogg, tidsskrift) med Tera i Rust. Artikkelmal + forside-mal per tema. CSS-variabler for theme_config-overstyring. Ref: `docs/concepts/publisering.md` § "Temaer". - [ ] 14.2 HTML-rendering av enkeltartikler: maskinrommet rendrer `metadata.document` til HTML via Tera, lagrer i CAS. Noden får `metadata.rendered.html_hash` + `renderer_version`. SEO-metadata (OG-tags, canonical, JSON-LD). - [ ] 14.3 Forside-rendering: maskinrommet spør PG for hero/featured/strøm (tre indekserte spørringer), appliserer tema-template, rendrer til CAS (statisk modus) eller serverer med in-memory cache (dynamisk modus). `index_mode` og `index_cache_ttl` i trait-konfig. -- [ ] 14.4 Caddy-ruting for synops.no/pub: statisk serving av CAS-rendret HTML. Rute `synops.no/pub/{slug}/{id}` → artikkel-CAS, `synops.no/pub/{slug}` → forside. Kategori/arkiv/søk rutes til maskinrommet for dynamisk respons. +- [ ] 14.4 Caddy-ruting for synops.no/pub: Caddy reverse-proxyer til maskinrommet som gjør slug→hash-oppslag og streamer CAS-fil. `Cache-Control: immutable` for artikler. Kategori/arkiv/søk serveres dynamisk av maskinrommet med kortere cache-TTL. - [ ] 14.5 Slot-håndtering i maskinrommet: `slot` og `slot_order` i `belongs_to`-edge metadata. Ved ny hero → gammel hero flyttes til strøm. Ved featured over `featured_max` → FIFO tilbake til strøm. `pinned`-flagg forhindrer automatisk fjerning. - [ ] 14.6 Forside-admin i frontend: visuell editor for hero/featured/strøm. Drag-and-drop mellom plasser. Pin-knapp. Forhåndsvisning. Oppdaterer edge-metadata via maskinrommet. - [ ] 14.7 Publiseringsflyt i frontend (personlig): publiseringsknapp på noder i samlinger med `publishing`-trait der `require_approval: false`. Forhåndsvisning, slug-editor, bekreftelse. Avpublisering ved fjerning av edge.