Dynamiq
Advanced

Caching

Cache node outputs in Redis so repeated runs with identical inputs skip execution — per-node opt-in plus a per-run cache config.

Node output caching stores the result of a node's execute in Redis, keyed by the node's id and a hash of its input. On the next run with identical input, the node returns the cached output without executing — no LLM call, no API hit. Caching is off by default and requires two switches: the node opts in, and the run supplies a cache backend.

Enable caching

  1. Per node — set caching=CachingConfig(enabled=True) on each node whose output you want cached.
  2. Per run — pass a cache config in the RunnableConfig. The only built-in backend is Redis, configured with RedisCacheConfig.
from dynamiq import Workflow
from dynamiq.cache import RedisCacheConfig
from dynamiq.connections import OpenAI as OpenAIConnection
from dynamiq.flows import Flow
from dynamiq.nodes.llms import OpenAI
from dynamiq.nodes.node import CachingConfig
from dynamiq.prompts import Message, Prompt
from dynamiq.runnables import RunnableConfig

llm = OpenAI(
    id="summarizer",
    connection=OpenAIConnection(),
    model="gpt-4o-mini",
    prompt=Prompt(messages=[Message(role="user", content="Summarize in one sentence: {{ text }}")]),
    caching=CachingConfig(enabled=True),
)

wf = Workflow(flow=Flow(nodes=[llm]))

config = RunnableConfig(
    cache=RedisCacheConfig(
        host="localhost",
        port=6379,
        db=0,
        namespace="my-app",
        ttl=3600,
    ),
)

text = "Dynamiq is an orchestration framework for agentic AI applications."
first = wf.run(input_data={"text": text}, config=config)   # executes the LLM
second = wf.run(input_data={"text": text}, config=config)  # served from cache

If either switch is missing — the node's caching.enabled is False, or the run has no config.cache — the node executes normally.

RedisCacheConfig

RedisCacheConfig combines the generic cache settings with the Redis connection fields:

backendCacheBackend
Fixed to "Redis" — the only built-in backend.
namespacestr | null
Optional prefix for all cache keys ("namespace:key"). Use it to isolate apps or environments sharing one Redis.
ttlint | null
Time-to-live in seconds for cache entries (stored via SETEX). Default null — entries never expire.
hoststrrequired
Redis host.
portintrequired
Redis port.
dbintrequired
Redis database number.
usernamestr | null
Optional auth username.
passwordstr | null
Optional auth password.

How keys and values work

The WorkflowCacheManager (dynamiq/cache/managers/workflow.py) builds the key as:

{entity_id}:{sha256(input_data)}:{sha256(kwargs)}
  • entity_id is the node's id — give cached nodes stable, explicit ids so cache entries survive process restarts.
  • The input dict is recursively key-sorted before hashing, so key order does not affect the hash. Any change to the input produces a different key.
  • Run-scoped kwargs (run_id, parent_run_id, wf_run_id, config, executor) are stripped before hashing, so every run of the same node with the same input maps to the same entry.

Values are JSON-serialized, Base64-encoded, and stored as Redis strings.

What a cache hit changes

The cache wraps execute_with_retry inside the node lifecycle:

  • A hit skips execute entirely — including its retry/timeout loop and execute-level callbacks.
  • Input transformation, schema validation, on_node_start / on_node_end callbacks, and the output transformer still run, so dependents and traces see a normal node result.
  • Callbacks receive is_output_from_cache=True on on_node_end, so you can tell hits from real executions in tracing.
  • Only successful outputs are cached — a node that raises stores nothing.

In async runs the same logic applies; cache reads and writes are offloaded to threads so they never block the event loop.

When to use it

Caching pays off for deterministic, expensive nodes called repeatedly with the same input: embedders during reprocessing, converters, scrapers, and LLM nodes with low temperature in batch pipelines. Avoid it for nodes whose output must reflect live state (retrievers over changing indexes, time-sensitive API tools) — or set a short ttl so entries age out.

Next steps

On this page