Caching

Skip redundant computation by caching node results. Same inputs produce the same outputs — hypergraph can remember that.

When to Use

  • Expensive computations you call repeatedly with the same inputs (embeddings, LLM calls)

  • Development iteration where you re-run a graph but only change downstream nodes

  • Batch processing where many items share common intermediate results

Basic Pattern

Mark a node with cache=True and pass a cache backend to the runner:

from hypergraph import Graph, node, SyncRunner, InMemoryCache

@node(output_name="embedding", cache=True)
def embed(text: str) -> list[float]:
    # Expensive API call — only runs once per unique input
    return model.embed(text)

@node(output_name="answer")
def generate(embedding: list[float], query: str) -> str:
    return llm.generate(embedding, query)

graph = Graph(nodes=[embed, generate])

runner = SyncRunner(cache=InMemoryCache())

# First call — embed executes normally
result = runner.run(graph, {"text": "hello", "query": "What is this?"})

# Second call with same text — embed served from cache
result = runner.run(graph, {"text": "hello", "query": "Different question"})

Two things are required:

  1. Node opt-in: @node(..., cache=True) on the nodes you want cached

  2. Runner backend: SyncRunner(cache=InMemoryCache()) or AsyncRunner(cache=...)

Cache Backends

InMemoryCache

Fast, ephemeral. Lives for the duration of the process.

DiskCache

Persistent across runs. Requires the optional cache dependencies:

This installs:

  • diskcache for Hypergraph's DiskCache backend

  • hypercache for optional InnerCacheEvent telemetry when your node body uses Hypercache internally

Integrity Verification

DiskCache stores serialized bytes plus an HMAC-SHA256 signature:

  • On write: value is serialized, signed, and stored with its signature

  • On read: signature is verified before deserialization

This prevents deserializing tampered cache payloads. If an entry is corrupted, missing a signature, has invalid metadata, or fails deserialization, Hypergraph evicts it and treats it as a cache miss.

Custom Backend

Implement the CacheBackend protocol for Redis, databases, or anything else:

How Cache Keys Work

Cache keys are computed from:

  1. Node identity — a hash of the function's source code (definition_hash)

  2. Input values — a deterministic hash of all inputs passed to the node

If you change the function body, the cache automatically invalidates. If inputs aren't picklable, the node falls back to uncached execution (with a warning).

Observing Cache Hits

Cache events integrate with the event system:

The event sequence for a cache hit is:

If your node body uses hypercache internally, Hypergraph also emits InnerCacheEvent entries for those nested cache decisions. Installing hypergraph[cache] gives you both Hypergraph's disk backend and the Hypercache observer bridge:

Caching Route and IfElse Nodes

Gate nodes (@route, @ifelse) are cacheable. The routing function's return value is cached, and the runner restores the routing decision on cache hit:

On cache hit, the runner replays the cached routing decision without calling the function again. Downstream routing still works correctly — the cached decision is restored into the graph state.

Restrictions

These node types reject cache=True at build time:

  • GraphNode — nested graphs have their own execution flow; cache individual nodes inside them instead

InterruptNode

InterruptNode supports cache=True (defaults to False). When cached, a previously auto-resolved response is replayed without re-running the handler.

Real-World Example: Cached RAG Pipeline

What's Next?

Last updated