01 โ Basic Linear Pipeline
A fixed sequence of steps: each step receives every state the previous step
produced, and the last step's states are the terminal results.
๐ฏ Goal
Normalize and sum a list of numbers with a three-node chain:
Clean โ Sum โ Announce.
๐งฑ Nodes
| from dagpipe import Node, State
class Clean(Node):
id = "math.clean"
def resolve(self, state: State):
numbers = [n for n in state.get("numbers", []) if isinstance(n, (int, float))]
yield self.fork(state, payload_update={"numbers": numbers})
class Sum(Node):
id = "math.sum"
def resolve(self, state: State):
yield self.fork(
state,
payload_update={"total": sum(state.get("numbers", []))},
)
class Announce(Node):
id = "math.announce"
def resolve(self, state: State):
print(f"total={state.get('total')}")
yield self.fork(state, payload_update={"announced": True})
|
๐ Run it
| from dagpipe import Engine, Payload, Schema, State
class CalcState(State):
schema = Schema({
"numbers": list,
"total": int | float | None,
"announced": bool | None,
})
engine = Engine([Clean(), Sum(), Announce()])
results = engine.run(CalcState(payload=Payload({"numbers": [1, "x", 2, 3.5]})))
state = results[0]
assert state.get("numbers") == [1, 2, 3.5]
assert state.get("total") == 6.5
assert state.get("announced") is True
assert len(state.history) == 3 # clean โ sum โ announce
assert state.depth == 3
|
๐ What just happened
- The engine seeded
states = [root].
Clean yielded one new state โ states = [clean_state].
Sum consumed it โ states = [sum_state].
Announce consumed it โ states = [announce_state] (terminal).
If any node yields zero states, the engine breaks and returns an empty list
โ that is how a pipeline can terminate early.
๐ก Tips
- Order in the sequence is the execution order โ keep stateful order in mind.
- Every node sees all states produced by the previous node. If a node forks
twice, the next step runs once per fork.
- Prefer
Node.fork over State.fork inside resolve() so the node ID lands
in history automatically.