← Products

Closed beta

Turn an estate of scattered systems into one queryable, AI-ready data fabric. NRV is a Python SDK for augmented DAGs over data.

Turn scattered instruments, archives and shared drives into one queryable, AI-ready data fabric. NRV is a Python SDK for augmented DAGs over data.

Turn an estate that cannot stop moving into one queryable, AI-ready data fabric. NRV is a Python SDK for augmented DAGs over data.

Turn decades of departmental systems into one queryable, AI-ready data fabric, inside your own tenant. NRV is a Python SDK for augmented DAGs over data.

Declare and Run

Declare where data lives, how it transforms, and where results land. NRV watches every source and propagates changes through the graph. No scheduler, no orchestration layer: the graph is the program.

Below is a whole working pipeline. It watches a source, keeps a ranked view in sync, and writes results as they change.


from nrv import NRV, Config, Sink, Source, Transform

sales = Source("configs/sales.json", alias="sales")

top = Transform("SELECT * FROM sales ORDER BY amount DESC LIMIT 10",
                sales, alias="top")

with NRV() as nerve:
    nerve.run(Sink(Config.from_file("configs/out.json"), top))

One estate, however many silos

A Source is a handle on something external: files, object stores, databases, warehouses, streams, HTTP and REST APIs. A Transform is SQL over upstream vertices; each upstream alias is a table name. Two silos that have never met join in four lines.

The engine survives run(). Results stay queryable in the same process.

NRV
The fabric. Owns the engine and the catalog, and outlives the run.
Source
An external resource being ingested, monitored for events.
Transform
Literal SQL over upstream vertices; induces a new vertex.
Materialize
Persists every update to an external target. Data keeps flowing downstream.
Sink
A terminal vertex, delivering data out of the graph.
Config
One JSON-round-trippable description of any resource. Secrets by reference only.

# a watched CSV directory, and a table in Postgres
sales   = Source("configs/sales.json",   alias="sales")
regions = Source("configs/regions.json", alias="regions")

joined = Transform("""
    SELECT s.*, r.name AS region_name
    FROM sales s
    JOIN regions r ON s.region_id = r.id
""", sales, regions, alias="joined")

revenue = Transform("""
    SELECT region_name, SUM(amount) AS revenue
    FROM joined
    GROUP BY region_name
""", joined, alias="revenue")

out = Sink(Config.from_file("configs/out.json"), revenue)

with NRV() as nerve:
    nerve.run(out)                      # changes flow to the sink
    nerve.sql("SELECT * FROM revenue")  # the engine outlives the run

Everything is an event

A file modification, a schedule tick, a Kafka message: the graph is an event-propagation network. Batch and streaming are the same program. Transforms fully recompute by default. Sources declaring cursor or append-only semantics flow incremental deltas instead.

Materialize is a structural decorator over any vertex. It persists every update to an external target, data still moving downstream. Layer boundaries, restart recovery and medallion architectures live there. Bronze to gold is a few declarations, not three pipelines. Each layer is a real table: location, format and write mode from its Config. It stays queryable inside the graph.


events = Source(
    Config(uri="memory://events",
           selection={"cursor": {"column": "id"}},
           events={"trigger": "push"}),
    alias="events",
)

def layer(name, mode, upstream):
    cfg = Config(uri=f"file://{LAYERS / name}",
                 format={"format": "parquet"},
                 write={"mode": mode})
    return Materialize(cfg, upstream, alias=name)

bronze = layer("bronze", "append", events)
clean  = Transform("SELECT * FROM bronze WHERE amount > 0",
                   bronze, alias="clean")
silver = layer("silver", "append", clean)
agg    = Transform("SELECT kind, SUM(amount) AS total "
                   "FROM silver GROUP BY kind",
                   silver, alias="agg", incremental=False)
gold   = layer("gold", "overwrite", agg)

Three surfaces, three trust levels

How the estate is queried depends on who is asking. sql() runs in your own process: raw and unvalidated. Restricting the process owner buys nothing. serve() exposes the estate over HTTP. There, queries are SELECT-only, table functions are refused, and references are restricted to the vertices you expose.

ask() gets exactly the same validation as the HTTP surface. Model output is untrusted, even in-process.


nerve.sql("SELECT * FROM carrier_safety")   # in-process, raw, trusted

nerve.serve(gold, port=8080)                # /v1/query, /v1/graph

nerve.ask("which carriers have the worst OOS rate?")   # validated

A semantic layer that stays out of the way

The core is deterministic: a graph of literal SQL, reproducible run to run. The semantic layer sits beside it, an authoring and analysis aid. It never executes inside the graph. No language model is ever in the execution path.

Point NRV at a catalog. Every source through it is fingerprinted, matched to a concept, and monitored for schema drift. No natural language required. Two silos declaring the same concept become one queryable idea. The optional plugin adds natural language on top. propose() never executes: a question becomes a pipeline only through a human reading the SQL.


nerve = NRV(catalog_path="./.nrv/catalog",
            provider=AnthropicProvider())

proposal = nerve.propose("worst carriers by out-of-service rate")
print(proposal.sql)          # review it, nothing has run

gold = proposal.as_transform(silver, alias="gold")   # into the graph

Install

Python 3.11 and up. The core installs the file drivers and nothing else. Optional drivers import lazily. A missing one raises an error naming the extra to install.

The package index is private during closed beta.


pip install nrv                        # core: file sources and sinks
pip install "nrv[all]"                 # + s3, postgres, kafka, http
pip install "nrv[semantic-anthropic]"  # + a language-model provider

Closed beta Write to us if you have an estate worth pointing it at.