This caches entire step outputs inside the workflow, keyed by each step’s actual inputs. It is different from Prompt Cache Optimization, which caches prompt prefixes at the LLM provider.
Quick Start
1
Enable with True
Pass
cache=True for a process-scoped LRU cache (default max_entries=128).2
Tune the cache size
Pass an
InMemoryStepCache instance to set capacity or share one cache across flows.3
Bring your own cache
Any object with
get() and set() works — back the cache with Redis, disk, or anything else.InMemoryStepCache is not re-exported at the top level. Import it from praisonaiagents.workflows.step_cache.When to Cache
Cache deterministic steps; skip steps that call tools with side effects, read a clock, or depend on randomness.How It Works
On a miss the step runs and its output is stored; on a hit the cached output is returned without an LLM call. The key is a stable SHA-256 (first 32 hex chars) built from four fields:Variable types are part of the key, so
True and "True" (or 1 and "1") never collapse to the same entry. Two runs with the same prompt but different variables are different calls.Correctness Guarantees
Opt-in only
Opt-in only
Nothing caches unless
cache= is set. Agent steps are not always deterministic, so silent caching would be wrong.Failures are never cached
Failures are never cached
Only successful steps are stored. Caching a failure would serve it again on every re-run, turning a transient error into a permanent one.
Both executors covered
Both executors covered
The linear executor and the pattern executor (used by
Parallel, If, etc.) are both wrapped, so caching works regardless of where a step sits.Bounded LRU
Bounded LRU
InMemoryStepCache caps at max_entries (default 128). An unbounded cache would be a memory leak on long-running flows.Thread-safe
Thread-safe
InMemoryStepCache uses an internal lock, so it is safe to share across Parallel branches.Deep-copied snapshots
Deep-copied snapshots
Values are deep-copied on
set and on get, so a caller mutating a returned value cannot corrupt future hits.A hit replays the full step delta
A hit replays the full step delta
On a cache hit the full step delta is replayed —
output, the step’s output_variable (or {step_name}_output), any handler-supplied variables, the step’s status and retries, and the StepResult.stop_workflow signal. Downstream {{step_name_output}} substitutions, ctx.variables[...] reads, and result["steps"][...] metadata all resolve the same way on cold and cached runs.A cached stop still stops
A cached stop still stops
A handler that returned
StepResult(stop_workflow=True) on the cold run also stops the workflow on the cached run — downstream steps never execute on the second run. With verbose=True the cache hit prints 🛑 Workflow stopped at: {step.name}, matching the cold-run message.Handler variables survive a hit
Handler variables survive a hit
StepResult.variables returned by the handler are cached and re-applied on a hit, so a downstream consumer reading ctx.variables["answer"] sees the same value on cold and cached runs. If guardrails rejected one attempt and accepted the next, the union of their variables is cached — matching what the live run accumulated, not just the last attempt’s keys.No spurious output variable on early stop
No spurious output variable on early stop
If the cold run stopped before writing
<step>_output (because stop_workflow=True), the cached hit does not insert it either — variables keeps the exact shape the cold run produced.These examples do not call an LLM — they exercise the handler-only path to make the guarantee concrete.
InMemoryStepCache Options
Public attributes:
hits, misses. Public methods: get, set, clear, __len__.
Accepted cache= values:
Seeing Cache Hits
Turn onverbose to print a line for each hit.
Best Practices
Cache deterministic steps only
Cache deterministic steps only
Formatters, lookups, and summarisers over unchanged text are safe. Their output depends only on their input.
Skip steps with side effects
Skip steps with side effects
Do not cache steps that call tools with side effects or read a clock — a cached answer would skip the effect the caller expected.
Use variables to force a re-run
Use variables to force a re-run
Change a workflow variable when you want a fresh call — different variables produce a different key.
Bound the cache to your working set
Bound the cache to your working set
Set
max_entries to the number of results you actually reuse; a larger cache only wastes memory.Related
Workflows
Build multi-step agent pipelines with AgentFlow.
Prompt Cache Optimization
Different layer — provider prompt-prefix caching.

