Add CI workflow, full env docs, fix Up button navigation
Some checks failed
Build and Push / build (push) Failing after 2m15s
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:
29
.gitea/workflows/build.yaml
Normal file
29
.gitea/workflows/build.yaml
Normal 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
|
||||||
63
README.md
63
README.md
@@ -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 (8K–128K 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
|
||||||
|
|
||||||
|
|||||||
@@ -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"}
|
||||||
|
|||||||
@@ -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 (8K–128K 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
|
||||||
|
|||||||
Reference in New Issue
Block a user