Fullfør oppgave 7.1: faster-whisper Docker-oppsett for norsk STT

Satt opp faster-whisper-server (fedirz/faster-whisper-server:latest-cpu)
som Docker-tjeneste på produksjonsserveren. Ingen GPU tilgjengelig —
bruker CPU med int8-kvantisering og large-v3 modell for best norsk kvalitet.

Verifisert:
- Transkripsjon fungerer via OpenAI-kompatibelt API
- verbose_json med segmenter og tidskoder OK
- Docker DNS-oppslag fra sidelinja-net fungerer
- Maskinrommet har WHISPER_URL=http://faster-whisper:8000
- RAM-bruk ~2.5 GB med modell lastet

Konfigurasjon:
- Image: fedirz/faster-whisper-server:latest-cpu
- Modell: large-v3 (norsk), int8, CPU
- CAS montert read-only for direkte filtilgang
- Healthcheck via python3 (curl ikke tilgjengelig i image)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
vegard 2026-03-17 17:21:29 +01:00
parent 8613b90f2c
commit 443f60a518
5 changed files with 134 additions and 3 deletions

View file

@ -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

View file

@ -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

View file

@ -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]
```

View file

@ -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=<generert av spacetime identity 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

View file

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