Quick Start
Enable with True
cache=True for a process-scoped LRU cache (default max_entries=128).Tune the cache size
InMemoryStepCache instance to set capacity or share one cache across flows.Bring your own cache
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.Durable Resume (FileStepCache)
FileStepCache is a drop-in durable replacement for InMemoryStepCache — it keeps cached step results in a JSON file so an interrupted flow resumes from where it stopped in a new process.
FileStepCache is not re-exported at the top level. Import it from praisonaiagents.workflows.step_cache.Start a long flow
cache=FileStepCache("run.json"). Each successful step’s result is written atomically to run.json.Get interrupted
Re-run the same command
Edit a step and re-run
Changed a tool? Clear first
FileStepCache("run.json").clear() before re-running.When to pick which
Pick the lightest cache that covers where your results need to live.What invalidates a cached step
Resume is content-addressed, not index-based, so a step re-runs whenever anything it depends on changes.Crash-safety and concurrent writers
Everyset() is a locked, atomic read-modify-write of the file, so a crash mid-write keeps the last good file. Two caches on the same path — in one process or two — both add to the file; neither erases the other’s entries. Only successful steps are cached, as with the in-memory cache.
FileStepCache Options
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 five fields: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
cache= is set. Agent steps are not always deterministic, so silent caching would be wrong.Failures are never cached
Failures are never cached
Both executors covered
Both executors covered
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
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
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
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
<step>_output (because stop_workflow=True), the cached hit does not insert it either — variables keeps the exact shape the cold run produced.InMemoryStepCache Options
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
Skip steps with side effects
Skip steps with side effects
Use variables to force a re-run
Use variables to force a re-run
Bound the cache to your working set
Bound the cache to your working set
max_entries to the number of results you actually reuse; a larger cache only wastes memory.Use FileStepCache for long runs and expensive steps
Use FileStepCache for long runs and expensive steps
Call clear() after editing a tool or a mutable dependency
Call clear() after editing a tool or a mutable dependency
Keep each run on its own path for strict isolation
Keep each run on its own path for strict isolation

