Control plane¶
The registry, run queue, and workers. See The control plane guide.
agentflow.controlplane
¶
Control plane: manage graph runs outside the process that created them.
An execution plane (CompiledGraph.invoke/stream/resume) runs a graph inline
in your code. The control plane adds a durable :class:RunQueue of run
requests, a :class:GraphRegistry (name -> compiled graph), and a
:class:Worker pool that claims requests and executes them — so runs survive a
process restart, scale across workers, and can be listed, cancelled, and
resumed from anywhere sharing the same queue and registry.
GraphRegistry
¶
Maps graph names to factories, caching one compiled instance per name.
The factory is called at most once per name; the resulting
:class:CompiledGraph is reused (it is immutable and safe to share across
concurrent runs on different threads).
Source code in agentflow/controlplane/registry.py
RunQueue
¶
Bases: Protocol
Persists run requests and hands them to workers under a lease.
Source code in agentflow/controlplane/queue.py
enqueue
async
¶
enqueue(graph: str, input: Mapping[str, Any] | None = None, *, thread: str | None = None) -> RunRecord
Add a new run for graph and return its record (status queued).
thread defaults to the generated run id.
Source code in agentflow/controlplane/queue.py
list
async
¶
list(*, status: str | None = None, graph: str | None = None, limit: int = 100, offset: int = 0) -> list[RunRecord]
Return run records, newest first, optionally filtered.
claim
async
¶
claim(*, lease_seconds: float = 60.0) -> RunRecord | None
Atomically take one runnable request and mark it running under a
fresh lease. Runnable means: queued, running with an expired lease, or
waiting (parked on ctx.wait) with its wake_at now past. Returns
None if nothing is available. Safe for concurrent workers.
Source code in agentflow/controlplane/queue.py
heartbeat
async
¶
complete
async
¶
Move a claimed run to a terminal-ish status (succeeded / failed /
interrupted / cancelled), clearing its lease. For a waiting status
(parked on ctx.wait), pass wake_at so the run becomes claimable
again once that time passes.
Source code in agentflow/controlplane/queue.py
request_cancel
async
¶
Request cooperative cancellation; return True if the run exists and is not already terminal. A queued run is cancelled immediately; a running one is flagged for the worker to stop at the next boundary.
Source code in agentflow/controlplane/queue.py
enqueue_resume
async
¶
enqueue_resume(run_id: str, value: Any = None) -> RunRecord
Re-queue an interrupted run with the human value so a worker
resumes it. Returns the updated record (status queued).
stats
async
¶
stats() -> QueueStats
Return a point-in-time snapshot of queue depth by status plus the count of expired-lease (stranded) runs — for health/monitoring.
MemoryRunQueue
¶
Keeps run records in a dict, guarded by a lock so claim is atomic.
Source code in agentflow/controlplane/memory.py
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | |
PostgresRunQueue
¶
Durable run queue backed by a PostgreSQL table.
Pass either a dsn or an existing asyncpg pool via pool.
table names the runs table (created on first use).
Source code in agentflow/controlplane/postgres.py
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 | |
close
async
¶
Close the connection pool (only if this queue created it).
Worker
¶
Executes runs claimed from a :class:RunQueue.
lease_seconds is the claim lease; heartbeat_interval how often it is
renewed (should be well under the lease). poll_interval is how long
run_forever waits when the queue is empty.
Source code in agentflow/controlplane/worker.py
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | |
WorkerPool
¶
Runs concurrency workers over one queue+registry.
Source code in agentflow/controlplane/worker.py
stop
async
¶
Signal workers to stop after their current run, then await them.
Source code in agentflow/controlplane/worker.py
health
¶
health() -> PoolHealth
Snapshot of the pool: configured size, live loops, and busy count.
A loop is "alive" if it was started and its task has not finished
(neither stopped cleanly nor crashed). busy counts workers mid-run.
Use health().healthy as a readiness signal — False means a worker
loop died and the pool is running degraded.
Source code in agentflow/controlplane/worker.py
RunRecord
dataclass
¶
One run's queue entry and status.
graph names a factory in a :class:~agentflow.controlplane.registry.
GraphRegistry; thread is the checkpointer thread the run executes on
(defaults to run_id). resume_value carries the human answer when an
interrupted run is re-queued. lease_until is the ISO time a worker's
claim holds until; an expired lease lets another worker re-claim (crash
recovery). cancel_requested is a cooperative flag a worker honors at a
super-step boundary.
Source code in agentflow/controlplane/records.py
RunStatus
¶
The lifecycle states of a run (string constants, JSON-friendly).
queued -> running -> succeeded | interrupted | waiting
| failed | cancelled. An interrupted run returns to queued
via enqueue_resume; a waiting run (parked on ctx.wait) becomes
claimable again on its own once wake_at passes; a queued/running
run can be cancelled.
Source code in agentflow/controlplane/records.py
QueueStats
dataclass
¶
A point-in-time snapshot of a run queue's depth, for monitoring.
by_status counts runs in every lifecycle state. expired_leases is
the number of running runs whose lease is past due — i.e. work stranded
by a crashed/stalled worker and awaiting re-claim. A rising value there is
the signal that workers are dying or falling behind their heartbeat.
Source code in agentflow/controlplane/records.py
PoolHealth
dataclass
¶
Health snapshot of a :class:~agentflow.controlplane.worker.WorkerPool.
workers is the configured size; alive counts worker loops still
running (not stopped and not crashed); busy counts workers currently
executing a run; idle = alive - busy. healthy is True when every
configured worker loop is alive.