◉ Pulse
N0819 avatar
#54 today ▲ 3

N0819/Fiction_Engine

Python AI

An engine for creating coherent fiction with LLMs

Created Jul 19, 2026
Stars
29
Today
+3
Forks
3
Quality
73/100
Momentum
16

README

Fetched from GitHub on Oct 30, 2026, 10:15 PM UTC · README.md

View original on GitHub

Third-party content — Open Source Pulse is not the owner of this repository.

This README belongs to N0819/Fiction_Engine and its contributors. We fetch it directly from the project’s public GitHub repository and display it for reference only. All text, code, images and trademarks remain the property of their respective authors. See the repository for its license terms. Copyright / takedown policy.

Sonder Engine

Discord

A local, single-player interactive-fiction engine in which the characters are separate minds rather than voices of one narrator.

Its defining constraint: no fictional mind may use information it did not legitimately perceive, learn, remember, or infer. Objective truth, perception, memory, belief, and narration are kept as distinct layers that never collapse into one context — which is what lets a character be wrong, be surprised, or be deceived, and mean it.

How a turn works

Each stage is a separate model call over a separate context. The Director owns objective causality; Perception decides what each observer legitimately receives; character agents choose behaviour from private context and never their own success; the Narrator renders only the player-facing slice; and persist/commit.py is the sole boundary where model output becomes persistent state.

director_interpret → mapping → perception_act
    → [reactions] → [character agents, in parallel or in an interaction loop]
    → director_resolve → background_react → perception_outcome
    → narrator → commit

Every stage's output is stored as a step/variant pair, so any turn can be rerolled, rerun from a stage, or hand-edited. docs/guides/PIPELINE.md has the exact flow, including the different opening-turn path.

Start here

  1. AGENTS.md — edit routing, invariants, source-of-truth order. Read first for any behavioural change.
  2. docs/guides/ENGINEERING.md — how the system operates, layer by layer, and why each boundary is where it is.
  3. docs/guides/PIPELINE.md — stage-by-stage execution.
  4. docs/guides/DATABASE.md — schema, write helpers, and the checklist every new persistent field must satisfy.
  5. docs/guides/MEMORY.md — what a character remembers: minting, provenance, ranking, unbidden recall, belief revision, embeddings.
  6. docs/CODE_MAP.md — generated index of modules, functions, routes, and tables. Regenerate with make map; never hand-edit.
  7. docs/guides/TESTING.md — test tiers and CI policy.
  8. docs/guides/FEATURES.md — every feature in plain language: what the app does, one line each.
  9. Design.md — philosophy, architecture, and a verified built / partial / not-built conformance table.
  10. docs/UNBUILT.md — the single register of known defects and unbuilt work. CHANGELOG.md is the history.

Run locally

Python 3.11, 3.12 or 3.13. Not newer yet — the pinned dependencies have no prebuilt wheel above 3.13, so pip falls back to compiling pydantic-core from source and fails inside a Rust toolchain.

Windows: double-click Start_Sonder.bat — it creates the environment, installs dependencies, and opens the app.

macOS / Linux: run ./Start_Sonder.sh — it does the same: finds a a supported Python (3.11-3.13), builds the environment, installs dependencies, starts the server and opens the app. It never touches a database. --port N, --host ADDR and --no-browser are there when the defaults do not suit; --help lists them.

By hand instead:

python3 -m venv .venv && source .venv/bin/activate
pip install -c constraints.txt -r requirements.txt
uvicorn web.app:app --host 127.0.0.1 --port 8008 --timeout-graceful-shutdown 3

That last flag is not optional in practice. Uvicorn's graceful shutdown waits indefinitely for a client that has not finished reading its response, and a browser tab buffering a multi-megabyte ambience bed is exactly such a client — so Ctrl+C prints "Shutting down", then "Waiting for connections to close", and sits there until a second Ctrl+C kills it. make run and make serve pass it for you.

Then open http://127.0.0.1:8008. The database is engine.db by default; set ENGINE_DB before startup to use another path.

To play, do not pass --reload (and make serve is the same command). Idle, the server itself costs nothing — measured at 0% of a core with a story open. The file watcher behind --reload is a different matter: given watchfiles it is event-driven and free, but without it uvicorn falls back to re-walking the whole tree and stat-ing every .py file four times a second, which measured 16% of a core, permanently, for a server doing nothing. requirements.txt asks for uvicorn[standard], which includes watchfiles, so a clean install of the above is fine — but a system-packaged uvicorn often is not. If you are developing and want reloads, check with:

python -c "import watchfiles" || pip install watchfiles

make run checks this for you and slows the fallback watcher down when watchfiles is missing.

Development

pip install -c constraints.txt -r requirements-dev.txt

make check       # compile + regenerate the map + structure + full tests — run before calling a change done
make check-fast  # the same, but only checks the map on disk is current instead of rewriting it
make test-full   # every Python regression test
make test-lf     # last-failed first, then the rest — the fix-verify loop
make structure   # duplicate symbols, patch debris, doc/version/path drift, import cycles, stale map
make map         # regenerate docs/CODE_MAP.md
make run         # start the server, watching for code changes
make serve       # start the server with no watcher — for playing

make check treats a stale docs/CODE_MAP.md, a duplicated top-level symbol, or leftover patch-debris markers as hard failures. Real-browser tests are optional (make test-browser); see docs/guides/TESTING.md.

Run everything from the repository root — the app uses absolute package imports such as from core.db import q and is not an installed package.

Layout

agents/          pipeline stages (director, perception, character, narration,
                 background) plus runtime, plan building, and shared helpers

core/            db, settings, jobs, pipeline context, install paths
llm/             providers, streaming, retries, embeddings; model-output
                 contracts (schemas.py) and system prompts (prompts.py)
world/           scene, spatial (a facade over its spatial_* siblings —
                 the import list is the roster), mechanics, survival,
                 comfort, weather, offscreen life, paradox
mind/            memory and retrieval, affect (mood, wants, intentions,
                 projects), psychology_runtime (stress, pain, pleasure),
                 theory_of_mind (belief)
story/           character and persona schemas, attire, greetings, importers,
                 crowds, couriers, artifacts
dressing/        backdrops and ambience
persist/         the sole persistence boundary — commit.py plus thirteen
                 commit_* domains, checkpoints, chat archive, pipeline trace
web/             FastAPI assembly, routes, streaming, auth

static/          browser UI — browser globals, not ES modules; load order matters
tests/           invariant and regression tests (505 files)
docs/            architecture documentation
tools/           maintenance scripts and experiment harnesses
language_packs/  per-language prompts, linguistics and compositor tables
extensions/      host-installed extensions; extension_runtime/ is their API

Providers and data

Providers can target OpenAI-compatible endpoints, Anthropic, Ollama, KoboldCpp, and configured remote services. Recall ranks a character's own memories in Python and deliberately uses no vector index: one character's rows are scored in NumPy and fused with a BM25 keyword ranking and an exact-phrase ranking, because the two filters that matter most — a mind may not retrieve how the turn it is deciding turned out, and it may not see another frame's memories — have to run before ranking, which is exactly what an approximate-nearest-neighbour index cannot do cheaply. At this workload the exhaustive scan is also simply cheaper than the problem: a few tens of milliseconds for a long story (docs/guides/RESEARCH.md §1.3–1.4).

Configuring an embeddings provider is what makes recall work by MEANING. Without one, the two vector rankings fall back to a character n-gram hash: a fuzzy lexical signature that retrieves reworded text well when it shares vocabulary, and not at all when it does not — measured against a real 441-memory story, recall of a genuine paraphrase is indistinguishable from random. Keyword and exact-phrase matching still work, so nothing breaks; what is missing is a character recalling something relevant that was worded differently three hundred turns ago.

Switching providers does not re-embed what is already stored — a vector can only be compared with one from the same model. The engine notices and offers to rebuild when you open a story, and API Connections shows the count and a button (docs/UNBUILT.md §1.15).

Prompt caching is a per-provider checkbox in API Connections, and it is off for any provider not known to forward a cache breakpoint — one that rejects it fails the turn, which is worse than not caching. It applies to Claude models only, because the caching is Anthropic's. Turning it on is not automatically a win: across 14 probed models a ~98% hit rate ran anything from 8× faster to 6.5× slower than no cache at all (docs/UNBUILT.md §1.25), which is why it is a switch and not a default.

API keys, provider settings, and all story content live in the local database — never commit a populated engine.db.

Credits

Prior art and research the engine draws on is sourced in docs/guides/RESEARCH.md. Named here because it is an external standard rather than a library:

  • Sigma Stratum — SIGMA Runtime documentation (sigmastratum.org). SRIP-14 §XXII, Retrieval as Perturbation Source, is the source of the idea that retrieval can serve divergence as well as recall — fetching contrasting rather than matching material when a mind has converged, and marking it non-authoritative. Public SRIPs are CC BY 4.0 with an Independent Implementation Safe Harbor. See docs/guides/RESEARCH.md §1.5 for what carries over and what deliberately does not.

    Implemented independently from the public specification, with thanks. Sonder isn't an official or certified Sigma integration, and nothing here speaks for Sigma Stratum — the good idea is theirs, any mistakes in using it are ours.

  • Freesound — the optional room-ambience feature can source its sound beds from Freesound's APIv2, the collaborative Creative Commons sound database maintained by the Music Technology Group at Universitat Pompeu Fabra, Barcelona. Every recording it plays is somebody's field work, uploaded under a licence that asks to be credited: the engine fetches only CC0 and Attribution sounds unless a host opts into more, stores the uploader, licence and source URL beside each cached file, and shows them in the 🎧 panel while that sound is playing. Sonder is not affiliated with or endorsed by Freesound, and using the feature needs your own free API key from freesound.org/apiv2/apply. If you publish or stream anything containing an Attribution-licensed bed, credit its uploader — the panel tells you who.

    The alternative source is a local folder of audio you already own, which involves no third party at all.

Two published findings changed what got built rather than what got imported, so they are ideas only — no code read, taken, or depended on. Lee, Goel & Ramchandran, Quantifying Positional Biases in Text Embedding Models: embedding models over-weight a text's opening sentence, which is why a minted memory leads with the beat's events and puts the room change last. Li et al., On the Sentence Embeddings from Pre-trained Language Models (BERT-flow, EMNLP 2020): cosine similarity tracks surface overlap more than meaning, so a memory omits unchanged standing state entirely rather than appending a delta to a fixed frame. Together they took the verbatim-twin rate in the memory bank from 14.6% to 0.4%. docs/guides/RESEARCH.md §1.6 has the detail, and names the leads deliberately not used — Angband's message aggregation is GPL and was not read, TADS 3 is proprietary and prohibits derivatives, Curveship's licence was never verified.

The weather overlay has no third-party dependency at all. It began as tsParticles by Matteo Bruni — a good library, and thanks are owed for the version that shipped first — but a particle engine redraws a full-screen canvas from JavaScript every frame, which is a great deal of power for scenery. It now draws one small tile, generated at runtime and seamless by construction, repeated as a background and moved by a CSS transform: no per-frame JavaScript, no repaint, and the animation runs on the compositor rather than the CPU. Nothing is downloaded and nothing is vendored.

License

Released under the MIT License.

Rendered by GitHub from the repository’s README. Scroll to read the full file.
First trending

Jul 20, 2026

first spotted on the list

Best rank

#11

highest position reached

Appearances

3

times on the trending list

Quality score

73

out of 100

Momentum

16

trend strength indicator

Star velocity

0.28

stars per day since creation

Activity — last 60 days

daily counts, collected every 6 hours
  • Stars
  • Forks
  • Merged PRs
  • Issues
  • Closed issues

Collecting activity data since launch — check back in a few days.

Star history

total stars per snapshot
5101520253035N0819/Fiction_Engine · Jul 20, 3 AM UTC: 12 starsN0819/Fiction_Engine · Jul 20, 9 AM UTC: 26 starsN0819/Fiction_Engine · Jul 20, 2 PM UTC: 29 stars3 AM8 AM2 PM

Rank history

position on trending · lower is better
#1#10#20#30#40#50#60Jul 20, 3 AM UTC: rank #28Jul 20, 9 AM UTC: rank #11Jul 20, 2 PM UTC: rank #543 AM8 AM2 PM

3D card for your website

Live HTML card with a glass surface and pointer tilt. Paste the iframe on any website.

<iframe src="https://www.notionpulse.cc/embed/N0819/Fiction_Engine" width="420" height="240" style="border:0;max-width:100%;background:transparent" loading="lazy" title="N0819/Fiction_Engine — trending on Open Source Pulse"></iframe>

Stars badge

Show current stars in your README or profile. Auto-updates every 6 hours.

N0819/Fiction_Engine stars badge
Markdown
[![N0819/Fiction_Engine](https://www.notionpulse.cc/api/badge/N0819/Fiction_Engine.svg)](https://www.notionpulse.cc/repo/N0819/Fiction_Engine)
HTML
<a href="https://www.notionpulse.cc/repo/N0819/Fiction_Engine"><img src="https://www.notionpulse.cc/api/badge/N0819/Fiction_Engine.svg" alt="N0819/Fiction_Engine stars badge" /></a>

Repository of the Day

#11 Repository of the Day

Repository of the Week

#11 Repository of the Week

Repository of the Month

#11 Repository of the Month