Compute graph orchestration with caching and observability
Project description
Cairns
A microframework for compute graphs with caching, tracing, and replay.
Think PyTorch for agent pipelines — though nothing about it is agent-specific.
You write ordinary async Python; a @step decorator turns each function into a
tracked, and optionally cached node in a graph that emerges from execution instead of being
declared up front.
Alpha. Public API names, the on-disk cache format, and the higher-order wrappers may still change between minor versions. Pin a version if you depend on it.
Why
Declarative graph frameworks (LangGraph, CrewAI, Airflow-style DAGs) force you into their node/edge DSL. Cairn goes the other way: the graph is your code, the framework just instruments it. From that you get:
- Caching keyed on
(function identity, body version, resolved args)— change one function, only its downstream re-executes. - Tracing — live, structured event log; a built-in TUI renders it.
- Resume — a failed pipeline reruns from the last successful step.
- Replay — cached runs can replay with original timing, indistinguishable from a live execution.
- Human-in-the-loop as a regular async step (
await_input(...)).
Works for LLM pipelines, scrapers, ETL, long-running research — anything that fits into mostly-pure async functions.
Install
pip install cairns[tui] # TUI is worth having
pip install cairns[full] # TUI + pydantic hashing
Or with uv:
uv add cairns --extra tui
Requires Python 3.12+.
Hello world
import asyncio
from cairns import step, run, trace
@step(memo=True)
async def fetch(url: str) -> str:
trace("fetching", state="running")
await asyncio.sleep(0.2) # pretend HTTP
return f"<html>{url}</html>"
@step
async def extract(html: str) -> int:
return len(html)
@step
async def pipeline(urls: list[str]) -> list[int]:
pages = [fetch(u) for u in urls] # returns Handles; runs concurrently
return [await extract(p) for p in pages] # pages are awaited inside extract
run(pipeline, store_path=".cairns",
args=(["https://a", "https://b", "https://c"],))
Run it twice. The second run is instant — every @step(memo=True) result is
looked up by cache key. Edit the body of extract, rerun: only extract
re-executes, fetches are cache hits.
CLI
cairns examples/research_fake_llm.py # run the pipeline, opens TUI if installed
cairns examples/research_fake_llm.py slow # run the `slow` entry point
cairns examples/research_fake_llm.py -f # clear this entry's cache, then run
cairns # interactive run browser over past runs
cairns list # flat list of runs
cairns show [RUN_ID] # print a trace (latest if omitted)
cairns gc [--before YYYY-MM-DD] # garbage-collect old runs
Default store is ./.cairns/. Override with --store PATH (or -s). Entry
points default to a function named main; pass a second positional arg to
pick another, e.g. cairns script.py my_pipeline.
Examples
Each example runs standalone with python examples/<name>.py, or through the
CLI with cairns examples/<name>.py for the TUI.
| Example | What it shows |
|---|---|
scraper.py |
Fan-out + chain, non-AI, fully mocked. Good first look. |
failure_resume.py |
A step fails on item 3; rerun resumes from cache. |
research_fake_llm.py |
Fan-out across N subjects, retry loop, rate limiting, simulated 20% API failure rate. No API key needed. |
hitl.py |
await_input inside a step — TUI input widget, stdin fallback. |
research_haiku.py + claude.py |
Live research over real AI companies via the claude CLI (Haiku). Cached by ISO week — re-runs within the week are free. |
What you'll see
With cairns[tui] installed, the CLI opens a live span tree: each @step
invocation is a row, child steps indent, trace(...) calls attach as
annotations, and cost={...} kwargs get summed up the tree. Failures colour
red, running steps pulse, completed steps show wall time + own time (excluding
waits on children).
Without the TUI, the same events stream to .cairns/runs/{entry}-{ts}/trace.jsonl
and you can read them with cairns show.
Docs
docs/motivation.md— the problem and the analogy to PyTorch.docs/design.md— all primitives, event log, stores, plugin points.docs/patterns.md— comparison against Prefect, LangGraph, Temporal, Flyte, CrewAI across seven patterns.
Status
Alpha, but the core works:
@step,Handle,trace,cached_output/tracing,replayable,rate_limited,await_inputall shipped.- File-backed content-addressed store, JSONL trace sink, symlinked run layout, GC.
- Live TUI for span-tree viewing.
- 110 tests, covering core + hashing + disk + resume + GC + metrics + patterns.
Possible future work: a web UI, distributed execution, distributed cache store.
Feedback and breakage reports welcome via issues.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cairns-0.2.0.tar.gz.
File metadata
- Download URL: cairns-0.2.0.tar.gz
- Upload date:
- Size: 271.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2c9fe505da5e8ce90cfba4841779004a43fc525d2525053763b47c6843098846
|
|
| MD5 |
3f84a772921bab509da8381becf236f6
|
|
| BLAKE2b-256 |
0460ceb7598bfd0a9ce74a684efa1e0e4fa59385b390cff4ca6cc5f6089840d2
|
File details
Details for the file cairns-0.2.0-py3-none-any.whl.
File metadata
- Download URL: cairns-0.2.0-py3-none-any.whl
- Upload date:
- Size: 69.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4300e2016c36b0a98ab97dad278ca7fdbad42bee022eff415a4b7aa24014fefc
|
|
| MD5 |
9cfe5dc44d4f7a2ab1524e674c52845d
|
|
| BLAKE2b-256 |
9c783c35445b057d43b218cba702720dc745871e58c2c96f33585e25eb67b5ec
|