An AI-powered RPG world simulation where a Gemini LLM acts as the world transition engine (intent → plan → diff → narration), not a chatbot. The world has persistent state, NPCs with goals/memory, a supply/demand economy, rumors that propagate and mutate, and emergent quests. A vanilla HTML/JS frontend renders the live world graph via Cytoscape.
https://github.com/subho004/llm-rpg-world-simulator/blob/main/assets/demo.mp4
demo.mp4
🎮 Player's guide → docs/GAME_GUIDE.md — how to play,
actions, the quest loop, and how to complete quests.
📖 Full manual & feature reference → docs/MANUAL.md — setup,
architecture, every subsystem, the complete API, the UI guide, configuration,
testing, troubleshooting, and how to extend.
Built on Python 3.14.2 + uv,
FastAPI, async SQLAlchemy 2.x (SQLite, swappable to Postgres), and google-genai.
Player action / Tick
→ parse intent / NPC plan (LLM, structured JSON via response_schema)
→ validate (deterministic rules; DiffApplier allow-lists + clamps)
→ apply effects (only deterministic code mutates state)
→ economy / rumors / quests (deterministic triggers → LLM phrasing)
→ narrate (LLM, concise)
→ WorldDiff + narration
The LLM only ever emits minimal diffs / structured intents; every change
passes through the DiffApplier choke point before touching the DB.
Copy .env.sample to .env. Set GEMINI_API_KEY to run with real Gemini, or
leave it blank (or LLM_ENABLED=False) to run fully offline with a
deterministic FakeLLMClient. Default model is gemini-3.1-flash-lite.
uv run uvicorn main:app --reload- Frontend (live world graph):
http://127.0.0.1:8000/ui/ - Health:
http://127.0.0.1:8000/health
Click Tick to advance the world, type actions ("travel to Oakvale", "buy a sword", "talk to Mira"), toggle Auto, or Reset to re-seed. Click any graph node to inspect an NPC's memories, relationships, and last rationale.
| Method | Path | Purpose |
|---|---|---|
| GET | /world |
world snapshot (clock, locations, counts) |
| GET | /world/graph |
Cytoscape graph JSON |
| GET | /world/market |
market prices |
| POST | /world/tick |
advance one tick → diff + narration |
| POST | /world/reset |
re-seed the world (optional {"seed": N}) |
| GET | /world/snapshot |
export the whole world as JSON |
| POST | /world/restore |
restore a previously exported snapshot |
| POST | /actions |
submit a player action |
| GET | /npcs, /npcs/{id}/details |
NPC list / detail |
| GET | /quests, /quests/{id}/details |
quest list / detail |
| POST | /quests/{id}/complete |
deliver the required item to complete a quest |
| GET | /rumors |
rumors + spread map |
All "good-to-have" features from the implementation plan are implemented:
- High value: auto-tick + adjustable speed, save/load snapshots (💾/📂), deterministic seed control, diff-flash + narration log, "Why?" inspector (an NPC's last rationale + the memories that drove it).
- Medium effort, high wow: NPC reflection (periodic memory summarization), rumor truth-decay visualization, relationship dynamics (affinity shifts from interactions; cliques surface via NetworkX community detection), weather/ season effects on economy + NPC mood, day/night NPC schedules.
- Larger: LLM cost guardrails (per-tick call budget + plan cache for
unchanged NPCs), deterministic combat resolver (wound/flee/defeat, narrated
by the LLM), and an eval harness that replays a fixed sequence and asserts
world invariants (
uv run python scripts/eval_harness.py).
Deliberately deferred (each a substantial project): vector/semantic memory, multiple kingdoms + politics, multi-agent NPC negotiation, Ollama local-model fallback, multiplayer, and SSE/websocket streaming narration.
WORLD_SEED, MAX_NPC_CONCURRENCY, LLM_TIMEOUT_SECONDS,
REFLECTION_EVERY_TICKS, and MAX_LLM_CALLS_PER_TICK (0 = unlimited) tune the
simulation. See .env.sample.
This project is built on a reusable FastAPI template using Python 3.14.2 and
uv for fast, reproducible dependency management.
Install uv (see the docs):
curl -LsSf https://astral.sh/uv/install.sh | sh- Create a virtual environment with the pinned Python version:
uv venv --python 3.14.2- Activate it:
source .venv/bin/activate- Install dependencies:
uv pip install -r requirements.txtStart the FastAPI server with Uvicorn:
uv run uvicorn main:app --reloadThen hit the health check at http://127.0.0.1:8000/ or http://127.0.0.1:8000/health.
If your FastAPI app is located in a different module, update
main:appaccordingly.
uv run pytest -vBuild and run the API with Docker Compose:
docker compose up --buildThe image uses the python:3.14.2-slim base and installs dependencies with uv.
uv runexecutes commands inside the project environment automatically — no manual activation needed.- For production deployments, replace
--reloadwith a production-ready configuration.