Execution graphs
Execution graphs
Wrap work in a task() scope and the SDK records a DAG of everything
that happened inside it: LLM calls and your own steps, auto-parented, with status, timings,
tokens, cost, and a rules-based failure classification.
async def research(client): with client.task("market-research") as t: await client.messages.create( model="auto", messages=[{"role": "user", "content": "Who are the top 3 competitors?"}], ) try: with client.step("fetch-pricing"): raise TimeoutError("pricing API timed out") except TimeoutError: pass # a step can fail without killing the whole task # Re-runs only the failed step, with a rebuilt minimal context, not a # replay of the whole transcript. The retry lands in the same graph, # linked to the node it replaces via depends_on. response = await client.resume(t.task_id) return client.get_task_graph(t.task_id)
It works in any framework, with no integration code, because parenting uses context variables rather than a wrapper API.
Failure classification
A failed node is labelled by a rules-based classifier rather than left as a raw traceback. The reasons are:
| Reason | Meaning |
|---|---|
context_overflow | The request exceeded the model's context window |
budget | A provider rate-limit, quota, or billing error: an HTTP 429 lands here |
tool_error | A tool or step raised |
stall | Three consecutive failed same-name siblings: a retry loop |
refusal | The model declined to answer |
unknown | Nothing matched |
LangGraph
A zero-touch LangGraph
adapter is included, which mirrors super-steps into the same task graph. Replace
MemorySaver() with FluxCheckpointer(client):
from fluxcompute.integrations.langgraph import FluxCheckpointer app = graph.compile(checkpointer=FluxCheckpointer(flux_client))
The adapter requires LangGraph itself: pip install langgraph.
The adapter mirrors only successful checkpoint writes. When a LangGraph
node raises, LangGraph never calls put()/aput() for that step, so
the adapter has no hook to observe the failure and it is never recorded in the task graph,
including the failure reasons above. If you need failure visibility for a failure-prone
node, wrap that node's body in client.step() directly; its auto-parenting works
fine alongside FluxCheckpointer.
Resume, and what durability costs
Recording and resume are both free and fully offline.
client.resume() works whenever the process that ran the task still holds its
graph, with no network call beyond the provider request itself. For a worked run, the
company brain example injects a
retired model id at one step and resumes exactly that call in the same graph.
Durability. If the process that ran the task has since exited, resuming means
reconstructing the graph from wherever it was persisted, which needs telemetry to have been
on and the hosted platform to have kept a copy. The two ways that can be missing raise
different things: with no recovery plugin registered, client.resume() on a task
from a dead process raises FluxRecoveryNotInstalled; with a plugin registered
that cannot find the task, it raises KeyError, the same as an unknown
task_id in-process. See
fluxcompute.dev for the durable-recovery
and dashboard offering.