Skip to content

narrative-graph logo

narrative-graph

Turn prose into candidate relational facts, entirely local. No LLM, no network. Entities, relations, confidence scores and spans, for Rust and Node.


CI CodeQL OpenSSF Scorecard License Code of Conduct

npm version

npm downloads

crates.io version docs.rs license stars

Install · What It Extracts · API · Guides · Contributing


narrative-graph reads prose and returns candidate (subject, relation, object) facts with a confidence score and the exact source span each one came from. No API key, no network call, no model download. It stores nothing and queries nothing — it's a producer you point at any store, or at no store at all.

The default build has zero dependencies. cargo tree prints the crate and nothing else; the extraction core compiles against the standard library alone, and CI fails if that ever stops being true. Serde, TypeScript bindings, the Node addon, and the CLI are each opt-in features that serve a boundary, never the core.

Input:

Elena is Marco's sister. Marco mentors Dev, who works at the Archive.

Output (measured, cargo run --features cli -- extract -):

dev --works_at--> archive 0.75 [39..68]
elena --sister_of--> marco 0.85 [0..14]
marco --mentors--> dev 0.78 [25..42]

Works with Rust 1.77+ and Node.js 18+, on macOS, Linux, and Windows. Pairs naturally with holographic-memory's Meaning Memory — memorizeTriplet / relate_phase expect exactly this shape — but has no dependency on it and no opinion on what you do with a candidate.

Install

npm

npm install narrative-graph

Cargo

[dependencies]
narrative-graph = "0.1"

GitHub

npm install writerslogic/narrative-graph

Cargo features

Feature Adds Dependencies
(default) The heuristic extraction pipeline none
serde Serialize/Deserialize on the public types serde
json JSON output serde, serde_json
bindings TypeScript declarations via ts-rs serde, ts-rs
node-api The Node addon napi, napi-derive, napi-build
cli The narrative-graph binary clap, anyhow

What It Extracts

Sentences

Segmentation is hand-rolled rather than a naive split on ./!/?, because narrative prose is made of exactly the constructs a naive splitter breaks on: honorifics (Dr. Smith went home.), initials (J. R. R. Tolkien), ellipses (She paused... then left.), and quoted dialogue with attribution ("Who are you?" she asked. is one sentence, not two). Sentences are returned as borrowed slices of the input, so segmentation allocates nothing per sentence.

Entities

Capitalized-token detection, pronoun-antecedent linking within a sentence, and an optional caller-supplied alias list so "Marcus" and "Mr. Marcus Hale" resolve to the same entity when you already know that. A title leading a name is dropped from the identity but kept in the surface form, so "Lady Catherine de Bourgh" and "Catherine de Bourgh" already unify without an alias.

You supply: { aliases: { "Marcus": "marcus_hale", "Mr Marcus Hale": "marcus_hale" } } Result: either mention resolves to marcus_hale in every candidate.

Keys match the mention's surface form exactly, and . is a separator, so the key is Mr Marcus Hale. A lowercase phrase like "the detective" is never detected as a mention, so it cannot be an alias key.

Relations

Verb-phrase heuristics between two entity candidates in the same clause — familial, social, professional, and spatial relations ("sister of," "mentors," "works at," "located in"). Surface forms normalize to a small controlled vocabulary (sister_of, mentors, works_at, ...) rather than passing through raw verb phrases, so "trains," "is the mentor of," and "mentors" all collapse to the same relation type. Supply your own ontology map to override or extend the defaults.

Explainability

Every candidate reports the rule that produced it, not just a score — possessive-sister-pattern, verb-mentor-pattern, verb-works-at-pattern, relative-mentor-pattern, relative-works-at-pattern. A confidence number alone tells you nothing about why the pipeline believes something; the rule name does.

Confidence and Span

Every candidate carries a confidence score from the extraction rule's pattern strength and how close the subject and object are in the source text, plus the byte offset of its extracted span — a range covering the subject, the object, and the token that licensed the relation. Gate on a threshold, route low-confidence candidates to human review, or fetch source context for display.

API

Rust -- core extraction and types
Item What it does
extract_candidate_triples(text, &Options) Run the heuristic pipeline over a passage
Options { aliases, min_confidence, ontology } Alias map, confidence floor, relation-vocabulary overrides
TripleCandidate { subject, relation, object, confidence, span, rule } One candidate fact; span is a byte range into the input, rule names the pattern that produced it
CLI -- extract from a file without embedding the library
narrative-graph extract chapter.txt --min-confidence 0.6 --format json

Installed alongside the crate (cargo install narrative-graph --features cli provides the narrative-graph binary), the same way holographic-memory ships hms-admin and hms-eval as bins in its own crate rather than separate packages. Useful for shell pipelines, CI checks, and inspecting output before wiring the library into an application.

Node.js -- extraction and types
Function What it does
extractCandidateTriplesNapi(text, opts?) Run the heuristic pipeline over a passage

opts accepts aliases, minConfidence, and ontology. Full TypeScript definitions ship in the package (index.d.ts), generated from the Rust types via ts-rs — treat them as the source of truth over this table.

Guides

Requirements

  • Rust 1.77+ or Node.js 18+
  • No API key, no network access, no external service of any kind

Development

git clone https://github.com/writerslogic/narrative-graph.git
cd narrative-graph
cargo test                          # segmentation + heuristic pipeline
cargo test --all-features           # adds the ts-rs binding-export tests
cargo tree                          # should print this crate and nothing else
npm install && npm test             # node --test tests/node
npm run test:types                  # strict TS check against generated bindings

TypeScript definitions in bindings/ are generated from the Rust types via ts-rs — don't hand-edit them.

Why narrative-graph

Hand-written regex General-purpose NLP toolkit LLM-based extraction narrative-graph
Runs offline, no API key yes yes no yes
Tuned for narrative prose (dialogue attribution, familial/social relations) you build it no depends on prompt yes
Confidence score per candidate no model-dependent inconsistent across calls yes
Source span on every candidate you build it sometimes rarely yes
Cost per extraction free, but brittle free, general-purpose API cost + latency free, microsecond-scale
Output shape ready for a relational store you build it you build it you parse it yes -- matches memorizeTriplet/relate_phase

narrative-graph isn't trying to out-perform an LLM at open-domain relation extraction — it's trying to be the thing you reach for when a manuscript's characters and relationships need to become structured facts, offline, cheaply, and repeatably, with enough provenance on each candidate that a human or a downstream store can decide what to trust.

Contributing

We welcome contributions of all sizes. Check the issue tracker for good first issue labels, or see CONTRIBUTING.md for development setup.

The sentence-level pipeline is complete and useful on its own. Where the project goes next is tracked as enhancement issueswhole-document aggregation and contradiction detection are the highest-leverage next steps, and each issue states what needs deciding before any code. Design discussion on those is as valuable as a patch.

Areas where help is especially welcome:

  • Additional relation-verb patterns for non-English narrative conventions
  • Integration examples for stores other than holographic-memory

Security

Found a vulnerability? Please report it privately — see SECURITY.md.

License

Apache-2.0 © WritersLogic, Inc.

GitHub · npm · crates.io · Issues · Changelog

About

Turn prose into candidate relational facts — entity detection, relation labeling, confidence scoring, entirely local.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages