Workflows¶
The imbi.common.workflows package holds the workflow definitions that
imbi-api and the workflow engine (imbi-workflows) share. See
ADR 0021 for the design and
Workflows for the operator view, including
the table of step types.
| Module | What it holds |
|---|---|
graph |
The models of a workflow version: Graph, Node, Edge, Settings, and the validation result |
step_type |
StepType, the declaration of one step type |
registry |
One module for each step type, and STEP_TYPES |
validation |
validate(), the checks of a version |
walk |
The order of the steps of a run. The engine and the test run use it |
runtime |
The workflow_runtime Postgres schema helpers, such as append_event() |
history |
The run log in ClickHouse, published through Iggy |
Step types¶
registry.STEP_TYPES maps each type name to its StepType. Each module
in registry declares one STEP. A node with a type that is not in
STEP_TYPES fails validation.
A StepType declares:
| Field | Meaning |
|---|---|
type, category, label, description |
The name, the palette group (trigger, logic, action, agent) and the text in the editor |
params, output |
Pydantic models for the parameters and the output. The API serves their JSON schemas |
sample_output |
An example output that the editor shows |
ports |
The output port names. ('',) is one port. None means one port for each entry in params.branches |
trigger |
The type starts a run |
side_effect, idempotent |
The type changes something outside the run, and a second call is safe |
timeout_seconds, retries |
The defaults. A node can set its own |
required_permissions |
A function from the node parameters to the permissions that the activating user must have |
When a call of a step that has side_effect=True and idempotent=False
stops before its result is recorded, the engine does not call it again.
The step fails with outcome_unknown.
To add a step type:
- Add a module to
libraries/common/src/imbi/common/workflows/registry/that declaresSTEP. - Add the module to
STEP_TYPESinregistry/__init__.py. - Add the executor to
Executorsinapps/workflows/src/imbi/workflows/executors.py. Add a type to the registry only when the engine can run it.
Validation¶
validate(graph, settings) returns a ValidationResult with a list of
issues. A missing required parameter is a warning, so a draft can have
steps that are not configured. Every other issue is an error. The API
saves a version with its issues, but it activates only a version with no
issues. The API refuses to save a version that breaks one of the size
limits below (LIMIT_CODES).
| Limit | Value |
|---|---|
| Steps | 100 (MAX_NODES) |
| Connections | 200 (MAX_EDGES) |
| Graph size, as JSON | 65536 bytes (MAX_GRAPH_BYTES) |
Run history¶
The engine and imbi-api write each run event to Postgres in the run's
transaction. After the commit, history publishes the event to the
workflow_run_events Iggy stream and the run state to workflow_runs.
A failed publish does not fail the write. The imbi-api archive sweep
publishes again the rows that ClickHouse does not have.
API Reference¶
Graph¶
Graph ¶
Bases: BaseModel
Node ¶
Bases: BaseModel
Edge ¶
Bases: BaseModel
An edge from output port of source to target.
Settings ¶
Bases: BaseModel
ValidationIssue ¶
Bases: BaseModel
ValidationResult ¶
Bases: BaseModel
Step types¶
StepType
dataclass
¶
StepType(
type: str,
category: Category,
label: str,
description: str,
params: type[BaseModel],
output: type[BaseModel],
sample_output: dict[str, Any],
ports: tuple[str, ...] | None = ('',),
trigger: bool = False,
side_effect: bool = False,
idempotent: bool = True,
timeout_seconds: int | None = 300,
retries: int = 0,
required_permissions: Callable[
[dict[str, Any]], set[str]
] = _no_permissions,
)
One step type. Each module in :mod:.registry declares one.
describe ¶
Return the step type as the API serves it.
Source code in libraries/common/src/imbi/common/workflows/step_type.py
ports_for ¶
Return the output ports of a node of this type.
Source code in libraries/common/src/imbi/common/workflows/step_type.py
STEP_TYPES
module-attribute
¶
STEP_TYPES: dict[str, StepType] = {
(type): (STEP)
for module in (
manual,
schedule,
webhook,
wait,
model_decision,
email,
deploy,
rollback,
agent,
)
}
Validation¶
validate ¶
Return the issues of a workflow version.
Source code in libraries/common/src/imbi/common/workflows/validation.py
Walk¶
order ¶
Return the node ids in topological order.
Ties keep the order of wf_graph.nodes. Nodes on a cycle are not
in the result; activation refuses a graph with a cycle.
Source code in libraries/common/src/imbi/common/workflows/walk.py
advance ¶
advance(
wf_graph: Graph,
status: dict[str, str],
ports: dict[str, int | None],
) -> tuple[list[str], list[str]]
Return the ready steps and the steps that become skipped.
status is changed in place for the skipped steps. A pending node
with no incoming edge is a trigger that did not start the run; the
caller marks those skipped when the run starts.
Source code in libraries/common/src/imbi/common/workflows/walk.py
Runtime¶
append_event
async
¶
append_event(
conn: AsyncConnection[Any],
run_id: UUID,
event_type: str,
payload: dict[str, Any],
) -> None
Write the next event of a run. Call it in the run's transaction.
The UPDATE of last_seq locks the run row, so the sequence
has no gaps.
Source code in libraries/common/src/imbi/common/workflows/runtime.py
pause_bindings
async
¶
Disable the trigger bindings of a workflow that pauses.
Call it in the transaction that pauses the workflow.