Add CI workflow, full env docs, fix Up button navigation
Some checks failed
Build and Push / build (push) Failing after 2m15s

- .gitea/workflows/build.yaml: Gitea Actions — push latest on main
- docker-compose.example.yml: image-based pull, all NAT20_ vars shown
- settings.py: add NAT20_PLAYER_RECAP_STYLE / CUSTOM_PROMPT overrides
- README.md: full env var reference table with accepted values + rationale
- files.py: fix parent_path to avoid up-button bug; add path traversal guard
- Files.tsx: remove copy/paste/autoPlay, scope to campaign
This commit is contained in:
KansaiGaijin
2026-07-10 22:17:47 +12:00
parent bc7ac32de3
commit ef44155b5d
4 changed files with 136 additions and 38 deletions

View File

@@ -0,0 +1,29 @@
name: Build and Push
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: Login to Gitea Container Registry
run: echo "${{ secrets.GITEA_TOKEN }}" | docker login gitea.kansaigaijin.com -u ${{ gitea.repository_owner }} --password-stdin
- name: Build and push backend
uses: docker/build-push-action@v5
with:
context: ./backend
target: runtime
push: true
tags: gitea.kansaigaijin.com/${{ gitea.repository }}/backend:latest
- name: Build and push frontend
uses: docker/build-push-action@v5
with:
context: ./frontend
push: true
tags: gitea.kansaigaijin.com/${{ gitea.repository }}/frontend:latest

View File

@@ -40,39 +40,62 @@ A reference compose file using named volumes only (no host paths) is at
## Data layout ## Data layout
The app stores everything under `/data` (inside the container), which by default The app stores everything under `/data` (inside the container):
is a [bind mount](./docker-compose.yml) to a host path of your choice:
| Directory / File | Contents | | Directory / File | Contents |
|---|---| |---|---|
| `audio/` | Uploaded recordings and extracted audio | | `campaigns/{id}/audio/` | Uploaded recordings and extracted audio, organised per campaign |
| `transcriptions/` | Per-session transcript JSON files | | `campaigns/{id}/transcriptions/` | Per-session transcript JSON files |
| `notes/` | Generated notes (GM log + player recap) | | `campaigns/{id}/notes/` | Generated notes (GM log + player recap) |
| `app.db` | SQLite database (sessions, speakers, settings, jobs) | | `app.db` | SQLite database (sessions, speakers, campaigns, settings, jobs) |
## Configuration ## Configuration
### Prefilling the setup wizard ### Environment variables (`NAT20_*`)
Set `NAT20_*` environment variables under the `backend` service in Set these under the `backend` service in `docker-compose.yml` to prefill
`docker-compose.yml` — the wizard will pick them up as defaults: the setup wizard and override defaults. All are optional — the wizard
and Settings page can set them at runtime.
```yaml #### Transcription
environment:
NAT20_HF_TOKEN: "hf_..."
NAT20_WHISPER_MODEL: medium
NAT20_OLLAMA_HOST: http://localhost:11434
NAT20_WORLD_CONTEXT_PATH: /data/campaign-context.txt
```
See the `environment:` block in `docker-compose.yml` for the full list. | Variable | What it does | Values | Default | Notes |
|---|---|---|---|---|
| `NAT20_WHISPER_MODEL` | Whisper model size | `tiny` `base` `small` `medium` `large-v3` | `medium` | Larger = more accurate but uses more VRAM. `medium` fits most 6-8 GB GPUs; `large-v3` needs ~10 GB+ |
| `NAT20_WHISPER_COMPUTE_TYPE` | Compute precision | `int8` `float16` `float32` | `int8` | `int8` = fastest / least VRAM. `float16` = more accurate, more VRAM. `float32` = full precision, slowest |
| `NAT20_HF_TOKEN` | HuggingFace token for speaker diarization | `hf_...` | (none) | Required for speaker attribution. Must accept pyannote gated-model terms with the same account first |
#### LLM backend
| Variable | What it does | Values | Default | Notes |
|---|---|---|---|---|
| `NAT20_OLLAMA_HOST` | Ollama server URL | URL | `http://localhost:11434` | Set to your Ollama host |
| `NAT20_OLLAMA_MODEL` | Ollama model name | any model on your server | `qwen2.5:7b` | |
| `NAT20_API_BASE_URL` | OpenAI-compatible API base | URL | `https://api.openai.com/v1` | Uncomment and set to switch from Ollama |
| `NAT20_API_KEY` | API key | string | (none) | |
| `NAT20_API_MODEL` | API model name | string | `gpt-4o-mini` | |
#### Summarization
| Variable | What it does | Values | Default | Notes |
|---|---|---|---|---|
| `NAT20_CHUNK_WORD_TARGET` | Target words per summarisation chunk | number | `2500` | Long transcripts are split into chunks, each summarised separately, then combined. **Lower** = more LLM calls but finer granularity (good for models with small context windows). **Higher** = more context per chunk but may exceed the model's window. `2500` is safe for most models (8K128K context) |
| `NAT20_WORLD_CONTEXT` | Campaign context string | text | (none) | Injected into every summarisation prompt so the LLM recognises NPCs, places, and lore correctly |
| `NAT20_WORLD_CONTEXT_PATH` | Path to campaign context file inside container | container path | (none) | Alternative to `NAT20_WORLD_CONTEXT` for large campaign bibles you update independently |
| `NAT20_PLAYER_RECAP_STYLE` | Player recap format | `story` `diary` `bullets` `custom` | `story` | |
| `NAT20_PLAYER_RECAP_CUSTOM_PROMPT` | Custom prompt (only when style is `custom`) | text | (none) | |
#### Setup wizard
| Variable | What it does | Values | Default | Notes |
|---|---|---|---|---|
| `NAT20_ONBOARDING_COMPLETED` | Skip the setup wizard | `true` or `false` | `false` | Set to `"true"` after you finish the wizard once |
### Campaign context ### Campaign context
You can paste context directly in the Settings page, or point to a file You can paste context directly in the Settings page, or point to a file
inside the container using the `world_context_path` setting (or the inside the container using `NAT20_WORLD_CONTEXT_PATH`. The file version is
`NAT20_WORLD_CONTEXT_PATH` env var). The file path version is useful for useful for large campaign bibles that you update independently.
large campaign bibles that you update independently.
## Notes on hardware ## Notes on hardware

View File

@@ -25,6 +25,8 @@ _ENV_OVERRIDES = {
"chunk_word_target": "NAT20_CHUNK_WORD_TARGET", "chunk_word_target": "NAT20_CHUNK_WORD_TARGET",
"world_context": "NAT20_WORLD_CONTEXT", "world_context": "NAT20_WORLD_CONTEXT",
"world_context_path": "NAT20_WORLD_CONTEXT_PATH", "world_context_path": "NAT20_WORLD_CONTEXT_PATH",
"player_recap_style": "NAT20_PLAYER_RECAP_STYLE",
"player_recap_custom_prompt": "NAT20_PLAYER_RECAP_CUSTOM_PROMPT",
} }
_BOOL_KEYS = {"onboarding_completed"} _BOOL_KEYS = {"onboarding_completed"}

View File

@@ -1,26 +1,69 @@
# Example Docker Compose — uses a named volume so data persists across rebuilds # Example Docker Compose — copy to docker-compose.yml and customise.
# without needing a specific host path. Copy to docker-compose.yml and customise. # Build locally: docker compose build
# Pull pre-built: docker compose pull
# Run: docker compose up -d
services: services:
whisperx-base:
build:
context: ./backend
target: whisperx-base
image: nat20-whisperx-base:latest
restart: "no"
backend: backend:
build: # ── Pre-built image (pull from registry) ──────────────────
context: ./backend image: gitea.kansaigaijin.com/Jamie/Nat20-Notes/backend:latest
target: runtime # ── Or build locally (uncomment below) ────────────────────
image: nat20-backend:latest # build:
# context: ./backend
# target: runtime
restart: unless-stopped restart: unless-stopped
depends_on:
- whisperx-base
environment: environment:
# Skip setup wizard on container restart (or "false" to force re-run) # ── Setup wizard ──────────────────────────────────────
NAT20_ONBOARDING_COMPLETED: "true" # "false" (or omit) to show the wizard on first run.
# Set to "true" after onboarding completes.
NAT20_ONBOARDING_COMPLETED: "false"
# ── Transcription ─────────────────────────────────────
# Model size: tiny | base | small | medium | large-v3
NAT20_WHISPER_MODEL: medium
# Compute precision: int8 (fastest/least VRAM)
# | float16 (more accurate)
# | float32 (full precision, slowest)
NAT20_WHISPER_COMPUTE_TYPE: int8
# Required for speaker diarization (accept HF gated-model terms first)
# NAT20_HF_TOKEN: "hf_..."
# ── LLM backend (Ollama — local) ──────────────────────
NAT20_OLLAMA_HOST: http://localhost:11434
NAT20_OLLAMA_MODEL: qwen2.5:7b
# ── LLM backend (API — OpenAI-compatible) ─────────────
# Uncomment these to use a hosted API instead of Ollama
# NAT20_API_BASE_URL: https://api.openai.com/v1
# NAT20_API_KEY: "sk-..."
# NAT20_API_MODEL: gpt-4o-mini
# ── Summarization ─────────────────────────────────────
# Target words per chunk. Long transcripts are split into
# chunks, each summarized separately. Lower = more LLM
# calls but finer granularity. Higher = more context per
# chunk but may exceed the model's context window.
# Default 2500 works for most models (8K128K context).
# NAT20_CHUNK_WORD_TARGET: "2500"
# Campaign context injected into every summarization prompt.
# Inline string or path to a file inside the container.
# NAT20_WORLD_CONTEXT: ""
# NAT20_WORLD_CONTEXT_PATH: /data/campaign-context.txt
# Player recap format: story | diary | bullets | custom
# NAT20_PLAYER_RECAP_STYLE: story
# NAT20_PLAYER_RECAP_CUSTOM_PROMPT: ""
volumes: volumes:
# ── Data persistence ──────────────────────────────────
# Option A: Named volume (auto-managed, no host path needed)
- app_data:/data - app_data:/data
# Option B: Host bind mount (replace with your path)
# - /mnt/media/dnd-sessions:/data
# HuggingFace + Torch model caches (avoid re-downloading)
- hf_cache:/root/.cache/huggingface - hf_cache:/root/.cache/huggingface
- torch_cache:/root/.cache/torch - torch_cache:/root/.cache/torch
@@ -35,7 +78,8 @@ services:
- "8000:8000" - "8000:8000"
frontend: frontend:
build: ./frontend image: gitea.kansaigaijin.com/Jamie/Nat20-Notes/frontend:latest
# build: ./frontend
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
- backend - backend