Skip to content

runicPython graph OGM & Graph-RAG

Map Python classes to graph nodes and edges, version your schema like Alembic, and ground LLM answers in a knowledge graph — on FalkorDB, ArcadeDB, Neo4j, Memgraph, Apache AGE or Amazon Neptune.

runic — Python graph OGM for Cypher graph databases

What is runic? ​

runic is a Python graph OGM — an object-graph mapper for Cypher graph databases. You declare typed Node and Edge classes, and runic maps them to labels, relationship types, indexes and constraints, tracks your changes, and compiles a fluent query API down to Cypher. The same model code runs unchanged on FalkorDB, ArcadeDB, Neo4j, Memgraph, Apache AGE and Amazon Neptune (the PostgreSQL graph extension).

Two more layers ship in the same Python package:

  • Graph schema migrations — an Alembic-style migration engine for graph databases, with versioned revision scripts, a CLI, and rollback.
  • Graph-RAG — a Graph-RAG SDK that turns documents into a knowledge graph and answers questions over it with citations back to the source text.
bash
uv add "runic-py[falkordb]"     # or: pip install "runic-py[falkordb]"

runic requires Python 3.14+, is MIT-licensed, and is published on PyPI as runic-py.

Graph OGM — map Python classes to nodes and edges ​

Map classes to nodes, write a relationship, then traverse it with the fluent query builder — no raw Cypher:

python
from runic.ogm import (
    Field,
    Node,
    Relation,
    Session,
    alias,
    create_driver,
    select,
)

class Person(Node, labels=["Person"]):
    id: str = Field(primary_key=True)
    name: str
    email: str = Field(unique=True)
    friends: list["Person"] = Relation(
        relationship="FRIENDS",
        direction="OUTGOING",
        target="Person",
    )

driver = create_driver("falkordb", host="localhost", port=6379, graph="myapp")

with Session(driver) as session:
    alice = Person(id="alice", name="Alice", email="alice@example.com")
    bob = Person(id="bob", name="Bob", email="bob@example.com")
    session.add(alice)
    session.add(bob)
    session.relate(alice, Person.friends, bob)
    session.commit()

    # Traverse the social graph: everyone Alice is friends with
    p, f = alias(Person, "p"), alias(Person, "f")
    stmt = (
        select(p)
        .where(p.id == "alice")
        .traverse(Person.friends, to=f)
        .return_target(f)
    )
    friends: list[Person] = session.scalars(stmt)
    print([person.name for person in friends])   # ['Bob']
    # MATCH (p:Person)
    # WHERE p.id = $p0
    # MATCH (p)-[:FRIENDS]->(f:Person)
    # RETURN f

Graph schema migrations — versioned, replayable, reversible ​

Track graph schema changes as versioned revision scripts, the way Alembic does for SQL. Generate one with runic revision, then describe the change with op.* calls:

python
# runic/versions/3f9a12c1_add_person_email_index.py

def upgrade(op) -> None:
    op.create_range_index("Person", "email")

def downgrade(op) -> None:
    op.drop_range_index("Person", "email")

Apply and roll back from the CLI:

bash
runic revision -m "add person email index"   # scaffold the script above
runic upgrade                                 # apply pending revisions
runic current                                 # 3f9a12c1 — add person email index
runic downgrade base                          # roll back to an empty schema

Graph-RAG in Python — knowledge graphs with cited answers ​

Turn unstructured text into a knowledge graph, then ask questions and get answers grounded in cited source chunks — extraction, embedding, storage, and hybrid retrieval handled for you:

bash
uv add "runic-py[graphrag,falkordb]"   # Graph-RAG extras + a backend driver
python
from runic.ogm import create_driver
from runic.rag import GraphRAG, Ontology, RagSettings

settings = RagSettings()
driver = create_driver(
    "falkordb", host="localhost", port=6379, graph=settings.falkordb_graph
)

rag = GraphRAG.with_defaults(driver, settings=settings, ontology=Ontology.default())
rag.bootstrap_schema()
rag.ingest_text(
    "Ada Lovelace worked with Charles Babbage on the Analytical Engine.",
    source="inline-demo",
)

answer = rag.query("Who worked on the Analytical Engine?")
print(answer.text)
for citation in answer.citations:
    print(f"  - [{citation.source}] {citation.text[:80]}...")

Set OPENAI_API_KEY first (or point runic.rag at Ollama for a fully local run). mode="auto" picks a focused or broad retrieval strategy per question.

Supported graph databases ​

One model definition, seven backends. Switching graph database means changing the arguments to create_driver() — your models, queries, and application code stay the same.

Graph databaseInstall extraNotes
FalkorDBrunic-py[falkordb]Redis-based; also runs embedded and in-process for tests
Neo4jrunic-py[neo4j]Official Bolt driver
Memgraphrunic-py[memgraph]In-memory, over Bolt
ArcadeDBrunic-py[arcadedb]Multi-model, over Bolt
Apache AGErunic-py[age]Graphs inside PostgreSQL
Amazon Neptune Databaserunic-py[neptune]AWS-managed, over Bolt with IAM auth
Amazon Neptune Analyticsrunic-py[neptune-analytics]AWS-managed, HTTPS, native vector search

See Supported Drivers for connection options and per-backend capability differences.

Beyond the basics ​

These snippets barely scratch it. runic is built for the hard parts of real graph work — the things you hit on day two, not day one:

  • Multi-hop and variable-length traversals — chain .traverse() calls or use .traverse(..., hops=(min, max)) to walk org charts, dependency trees, and recommendation paths without hand-writing *1..5 Cypher.
  • Edge properties as first-class data — model the relationship itself with Edge, read it back with all_with_edges(), and filter on the edge.
  • Lazy vs. eager loading, on your terms — no hidden N+1 surprises; you decide what gets fetched and when.
  • Vector KNN and fulltext search — declare the index on your model and query it with vector_search() / fulltext_search() — native, not bolted on.
  • Async that mirrors the sync API — the same calls, await-ed.
  • Migrations that travel — the same upgrade/downgrade workflow runs unchanged across FalkorDB, ArcadeDB, Neo4j, Memgraph, Apache AGE, and Amazon Neptune.

Frequently asked questions ​

What is a graph OGM? ​

A graph OGM (object-graph mapper) is to graph databases what an ORM is to relational ones. Instead of writing Cypher and reading back raw rows, you declare typed Python classes; the OGM maps them to nodes, edges, labels and relationship types, tracks changes, and generates the queries. In runic you define models by subclassing Node and Edge and declaring Field and Relation attributes, and a Session handles reads, writes and change tracking.

Which graph databases does runic support? ​

FalkorDB, Neo4j, Memgraph, ArcadeDB, Apache AGE (the PostgreSQL graph extension) and Amazon Neptune — Database and Analytics. The same model and query code runs on all seven — see Supported Drivers.

Does runic support async Python? ​

Yes. AsyncSession and AsyncRepository mirror the synchronous API call for call, so async code is the same code awaited. There are no hidden lazy loads, so query patterns stay deterministic under concurrency. See the Async Guide.

What is Graph-RAG, and how does runic implement it? ​

Graph-RAG grounds LLM answers in a knowledge graph instead of a flat vector index, so retrieval can follow relationships between entities rather than only matching similar text. runic.rag ingests documents by chunking them, extracting entities and relations against an ontology, embedding them, and storing everything as a graph. Retrieval fuses graph traversal and vector search with reciprocal rank fusion, and every answer comes back with citations to its source chunks. Start with What is Graph-RAG?.

Can I still write raw Cypher with runic? ​

Yes. The query builder covers traversals, filtering, aggregation, paging, vector KNN and fulltext search, and every builder call documents the Cypher it compiles to. When you need something the builder does not model, run a raw Cypher statement through the same session and driver.

Do I need Docker to test code written with runic? ​

No. The testing guide uses an embedded, in-process FalkorDB, so unit tests covering CRUD, relationships and queries run without any external server or container. Docker is only needed when you want to test against a specific live backend.

Which Python version does runic require, and is it open source? ​

runic requires Python 3.14 or newer and is published on PyPI as runic-py. It is open source under the MIT license, developed at github.com/jenreh/runic.

Where to go next ​

Start with a quickstart and you'll have something running in five minutes:

Then go deep:

Bring your own backend. Write your models once. runic handles the Cypher.

runic — Python graph OGM, schema migrations and Graph-RAG for Cypher graph databases. · ImpressumAI generatedAI generated