Skip to content

About

A campaign-aware Dungeon Master's generative assistant.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

SAGE ✦ — Story Assistant & Generative Engine

A campaign-aware Dungeon Master's assistant. The DM maintains a campaign's state (party level, location, tone, NPCs, background lore, player characters) and uses it to generate context-grounded content — encounters, loot, NPCs, descriptions — so the AI stays consistent with your established story instead of being context-blind like a static random table. Generations are saved to a session log.


Features

YouTube demo - OUTDATED image

Feature Description
Multi-campaign Create and switch between campaigns from the header
AI generation encounter / loot / npc / description — all campaign-aware
World Bible Background story, key characters, player info — injected into every prompt as immutable lore
Session log All generations + DM journal notes, newest-first
Recent log context Last 8 log entries (≤ 3 000 chars) fed into each generation prompt
Export / Import Back up or migrate campaigns as JSON, CSV, or TSV
API flexibility OpenRouter · OpenAI · Anthropic · Gemini · Ollama · Custom — switch in-app, no restart
In-app API key Key stored in the local DB; no config file required

Stack

  • Backend: FastAPI + uvicorn (Python 3.12)
  • Database: SQLite (stdlib sqlite3), file at data/gm_assist.db, schema in data/schema.sql
  • Frontend: Vanilla JS + Tailwind CSS via CDN — no build step
  • AI: Configurable (default: OpenRouter → openai/gpt-4o-mini)

Quick Start (local)

git clone <repo>
cd B1_DM_assist

pip install -r requirements.txt

# optional: set a fallback key in .env (can also be set in the UI)
cp .env.example .env

uvicorn src.main:app --reload --port 8002
# → open http://localhost:8002

Open the Settings tab to choose your API provider and paste your key. The key is saved to data/gm_assist.db.


Database Structure

SQLite file: data/gm_assist.db — created automatically on first run. Schema source: data/schema.sql.

campaigns

Column Type Notes
id INTEGER PK auto-increment
name TEXT NOT NULL
system TEXT e.g. "D&D 5e", "Pathfinder 2e"
setting TEXT one-line world description
tone TEXT e.g. "dark & gritty", "whimsical"
party_level INTEGER default 1
current_location TEXT where the party is now
recent_events TEXT rolling DM notes on what just happened
known_npcs TEXT operational NPC list (names, roles, secrets)
background_story TEXT World Bible — core lore, history, prophecies
key_characters TEXT World Bible — major NPCs in depth
player_info TEXT World Bible — player characters, backstories
created_at TEXT datetime('now') UTC

Operational state (left panel) changes session-to-session. World Bible (centre tab) is the immutable foundation — always injected into AI prompts.

generations

Column Type Notes
id INTEGER PK
campaign_id INTEGER FK → campaigns.id
created_at TEXT UTC
kind TEXT encounter · loot · npc · description · note
source TEXT ai (generated) or dm (journal note)
dm_input TEXT what the DM described
title TEXT short punchy title
content TEXT main read-aloud text
mechanic TEXT dice / DCs / stats (empty string if none)
secret TEXT DM-only twist
connects_to TEXT tie-in to NPC / event
is_favorite INTEGER 0/1, default 0

settings

Global key/value store. One row per setting.

Key Default Description
api_provider openrouter Active provider
api_model openai/gpt-4o-mini Model name / ID
api_key "" API key (plaintext, local only)
api_base_url "" Override base URL (Ollama / Custom)

Configuration

Environment variables are fallbacks — the in-app Settings tab takes precedence.

Variable Used when Description
OPENROUTER_API_KEY provider = openrouter OpenRouter API key
OPENAI_API_KEY provider = openai OpenAI API key
ANTHROPIC_API_KEY provider = anthropic Anthropic API key
GEMINI_API_KEY provider = gemini Google Gemini API key
MODEL no DB model set Fallback model name
API_PROVIDER no DB provider set Fallback provider

Supported providers

Provider Base URL Notes
openrouter https://openrouter.ai/api/v1 Recommended — one key, all models
openai https://api.openai.com/v1
anthropic https://api.anthropic.com/v1 Uses native Messages API format
gemini https://generativelanguage.googleapis.com/v1beta/openai OpenAI-compat endpoint
ollama http://localhost:11434/v1 Self-hosted, no key needed
custom user-defined Any OpenAI-compatible endpoint

Deployment

Local — bare Python

pip install -r requirements.txt
uvicorn src.main:app --host 0.0.0.0 --port 8002

Docker Compose (recommended for self-hosting)

docker compose up --build
# → http://localhost:8002

The data/ directory is bind-mounted so the database survives container restarts. Set your API key via the in-app Settings tab.

Home NAS (Synology / QNAP / TrueNAS)

# docker-compose.yml — adjust volume path for your NAS
services:
  dm-assist:
    build: .
    ports:
      - "8002:8002"
    volumes:
      - /volume1/docker/dm-assist/data:/app/data   # Synology example
    restart: unless-stopped
docker compose up -d --build

Access at http://<nas-ip>:8002. For external access, forward the port in your router — or use a VPN for better security.

VPS / Online Server

git clone <repo> && cd B1_DM_assist
docker compose up -d --build

nginx reverse proxy:

server {
    listen 80;
    server_name dm.yourdomain.com;
    location / {
        proxy_pass         http://127.0.0.1:8002;
        proxy_http_version 1.1;
        proxy_set_header   Host $host;
        proxy_set_header   X-Real-IP $remote_addr;
    }
}

Add TLS: certbot --nginx -d dm.yourdomain.com

Add basic auth to restrict access:

htpasswd -c /etc/nginx/.htpasswd dmuser
# nginx server block: auth_basic "DM Assist"; auth_basic_user_file /etc/nginx/.htpasswd;

Cloud Platforms

Railway — railway init && railway up. Add a Volume at /app/data.

Render — Web Service. Build: pip install -r requirements.txt. Start: uvicorn src.main:app --host 0.0.0.0 --port $PORT. Add a Persistent Disk at /app/data.

Fly.io

fly launch
fly volumes create dm_data --size 1
# fly.toml: [[mounts]] source="dm_data" destination="/app/data"
fly deploy

Note: Cloud deployments are public by default. Add HTTP basic auth or a FastAPI auth middleware before sharing the URL.


Development

pytest -q                                          # run tests (no network — _call_model is monkeypatched)
uvicorn src.main:app --reload --port 8002          # dev server

About

A campaign-aware Dungeon Master's generative assistant.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages