Linear chains break down once you need branching, loops, or human-in-the-loop.Documentation Index
Fetch the complete documentation index at: https://cognis.vasanth.xyz/llms.txt
Use this file to discover all available pages before exploring further.
Graph<S> is Cognis’ answer: a stateful workflow where each step is a node that reads typed state and decides what happens next. Pregel-style execution, generic over your state struct.
What it is
A graph has three parts:- State — a struct of yours that travels between nodes. You implement
GraphState, which says how anUpdateis applied to the state. - Nodes — async functions that read the state and emit
NodeOut { update, goto }. - A start node — declared with
.start_at("name"). The engine walks edges from there until a node returnsGoto::end().
Runnable<S, S>, so it composes with everything else.
When to reach for a graph
- You need a tool-calling loop with custom termination logic.
- You want time travel — checkpoint after every step, rewind, branch.
- You need human-in-the-loop pauses at specific points.
- Branches need to run in parallel and rejoin.
- You want to subgraph and namespace state.
AgentBuilder is faster — it builds the graph for you. Drop down to graphs when the agent shape isn’t enough.
Quick example
A graph that increments a counter until it hits 5:node_fn::<S, _, _>(name, async closure) wraps an async closure into a Node<S>. The closure receives (&S, &NodeCtx) and returns Result<NodeOut<S>>.
State and reducers
Two ways to define state.- Manual impl
- Derive macro
Spell out how updates apply. Most explicit; works everywhere.
Control flow
Nodes pick the next step by returning aGoto:
| Constructor | Effect |
|---|---|
Goto::node("name") | Single successor. |
Goto::end() | Terminate. |
Goto::halt() | Pause without ending. |
Goto::Multiple(vec!["a", "b"]) | Parallel fan-out. |
Goto::Send(vec![("worker", payload)]) | Cross-graph dispatch. |
How it works
- Each
invokeis a sequence of supersteps. The engine runs all eligible nodes in a step, applies their updates atomically, then advances. Goto::Multipletriggers parallel fan-out. All targets run in the same superstep.- State is applied, not replaced. Your
apply(&mut self, Update)controls how. Reducers are justapplywritten for one field at a time. - Compiled graphs are
Runnable<S, S>. Wrappers (with_retry,with_timeout) and observability work the same as anywhere else.
Beyond the basics
- Persist state across runs? Add a checkpointer. See Graph workflows → Checkpointing.
- Pause for a human? Mark interrupts. See Building agents → Human-in-the-loop.
- Inspect intermediate state?
graph.get_state,get_state_history,update_state. See Graph workflows → Checkpointing. - Stream node deltas in real time? Graph workflows → Streaming.
- Visualize?
graph.to_dot(),to_mermaid(),to_ascii(). See Graph workflows → Visualization.
See also
Agents and the loop
The shape
AgentBuilder compiles into.Control flow
Fan-out, conditional edges, subgraphs.
Reference → cognis-graph
Full API surface.