Workflows¶
A FlowGraph workflow is a directed graph of typed nodes. Each node performs one step, such as selecting a file, reading data, transforming it, displaying a result, or writing it to disk. Connections pass values from a named output port to a named input port. The workflow engine is domain-independent: mesh and image operations are examples of adapter-provided nodes, not limits on the workflow model.
Pillow image workflows are supported by the Image Tools adapter. It can read, transform, display, and write images, while the mesh adapters provide the finite-element workflow capabilities described below.
For example, a mesh conversion workflow can be arranged as:
Select file ── path (Text) ──▶ Read Mesh (Muscat)
│ mesh (Mesh document)
├────────▶ To 3D View
└────────▶ Write Mesh (MeshIO)
The mesh document produced by a reader is FlowGraph's canonical editable mesh model. The 3D view is a derived visualization of that document, not a separate editable source.
Build and run a workflow in the application¶
- Add nodes from the Nodes menu or the node search dialog.
- Connect an output port to a compatible input port in the Workflow panel.
Ports are typed, so FlowGraph rejects incompatible connections unless an
explicitly registered automatic conversion is available. Initially, FlowGraph
converts
IntegertoFloatandFloattoText. Converted connections are amber; select one to see the conversion in the Node Inspector. See Edge Conversions for the complete behavior. - Expand a node and configure its parameters. A parameter that is connected to another node uses the incoming value for that execution.
- Select Edit → Auto Organize Nodes to arrange the active workflow from left to right according to its dependencies. Nodes in the same dependency layer are ordered to reduce crossing connections and evenly distributed on a regular grid. The action can be undone.
- Click Run work in the workflow toolbar below the application menus. Nodes execute in stable topological order. Use Immediate execution to automatically execute the selected node and its executable downstream nodes after changing one of its parameters.
- Select a node or edge to inspect its inputs, outputs, and execution state in the Node Inspector.
Use the orange nodes in the Workflow menu to define a reusable workflow interface. Give each Workflow Input and Workflow Output a unique name, then connect the input node's output to the graph and connect graph values to the output nodes. Those names become the inputs and outputs used by saved workflows, Python callers, and Run Full Workflow.
Use Run → Run Only Selected when only one node needs to be executed. If a node fails, successful validated results from upstream nodes remain available for inspection. Independent branches can continue, while nodes that depend on a failed result remain unexecuted.
Run workflows from the command line¶
Inspect the public interface, then provide the inputs you want to change:
flowgraph inspect workflow.json
flowgraph inspect workflow.json --full
flowgraph run workflow.json --input message=hello
Inspection is compact by default: it shows published inputs, their saved defaults,
published outputs, and any arbitrary-code warnings. Add --full or -f to include
the Workflow: graph section, for example flowgraph inspect -f workflow.json.
The full inspection graph lists nodes in dependency order, with disconnected nodes
first. A deterministic heuristic reorders ready nodes to reduce visible crossing
junctions, then vertical lane length, keeping the original layout on ties or when
the candidate is worse. This does not guarantee the global minimum and does not
change workflow execution order or saved node positions.
Within each node, ordinary inputs, parameters, and outputs remain separate
sections in that order. Connected inputs and parameters are reordered only
within their own section, visiting rightmost lanes first to reduce crossings.
Outputs are ordered by their last consumer, placing longer-lived lanes on the
left. These display permutations do not change port definitions or parameters.
Each connected output opens one shared vertical lane on its (O) line, with
all destinations listed as (to first.port, second.port). Intermediate consumers
branch from that lane with ├──▶; the last consumer closes it with └──▶.
Input labels retain (from source.port) to identify provenance where lines cross.
Unconnected outputs remain ordinary (O) lines without lanes. Connected
parameters show the incoming connection rather
than their inactive saved value. Nested workflows have separate connection lanes.
-i is an alias for --input; repeat either option for multiple inputs.
Values are decoded as JSON when possible, so count=0 and enabled=false
provide an integer and a boolean, while message=hello provides text.
Omitted inputs and saved parameter values¶
flowgraph inspect workflow.json shows each published input's resolved default
on the right of its input line, for example:
Inputs without a usable default show default: (none; required). Conflicting
downstream defaults show default: (conflicting saved values; provide --input);
inspection still displays the rest of the workflow. Defaults use the same lookup
as flowgraph run, including nested workflows and directly exported parameters.
Inspection does not execute nodes or emit omitted-input execution warnings.
Preview command-line values with the same assignments supported by run:
flowgraph inspect workflow.json -i message=hello --override target.value=updated
flowgraph inspect workflow.json --full --input message=hello
Overrides are applied in memory before resolving saved defaults. Explicit input
values take precedence and appear as default: 'hello' (--input) in the report.
Other inputs retain their resolved default, required, or conflict markers; you
do not need to supply every required input just to inspect a workflow. Repeated
assignments, unknown inputs/nodes/parameters, and incompatible explicit input
types are rejected just as for run. Full graph parameter lines reflect the
overrides; connected parameters still show their incoming connection rather than
an inactive saved value. No nodes are executed and the workflow file is unchanged.
You can omit an input when the workflow contains a usable saved parameter value for it:
For example, if a Workflow Input named message connects to target.value
and that node has the saved parameter value: "saved message", FlowGraph uses
"saved message" for the omitted input. It prints this warning to stderr:
flowgraph: Warning: Workflow input 'message' was not provided; using saved parameter value 'saved message' from target.value.
The lookup follows Workflow Input connections and published inputs inside nested
workflows. It also supports parameters exported directly as workflow inputs.
It reads parameter values without executing nodes to discover defaults. Values
such as 0, false, and an empty string count as provided defaults.
The precedence is:
- An explicit
--input NAME=VALUEalways wins; no fallback warning is issued for that input. - Node parameter
--override NODE-ID.PARAMETER=VALUEassignments are applied before omitted-input resolution, so they can change the fallback value. - Otherwise, an omitted input uses a compatible saved parameter value from its downstream target or directly exported parameter.
If multiple downstream parameters supply saved values, they must agree after
type normalization. Conflicting values produce an error asking for an explicit
--input. If no usable saved value exists, the command reports
Missing required workflow inputs. Ordinary data ports do not acquire a default
merely because they are connected to a Workflow Input node.
Fallback values must satisfy the published input type, with registered value conversions applied where available. FilePath values retain their dependent files; float 3D vectors accept saved numeric components and normalize them to floats.
Warnings identify the input, chosen value, and source parameter address. Nested
addresses include their parent node, for example nested/target.value. Public
outputs stay on stdout, or go to the file selected with --json-output:
Input fallbacks and overrides affect only this execution; they do not rewrite
the workflow JSON. This fallback behavior belongs to flowgraph run and
execute_cli_workflow(); it does not change the input requirements of
WorkflowGraph.execute() or other execution APIs.
Save and load workflows¶
Use File → Save workflow to write the graph to JSON and File → Load workflow to reopen it. A saved workflow contains:
- the
flowgraph-workflowformat ID and version; - node IDs, definitions, parameters, and canvas positions; and
- port-addressed connections between nodes.
The JSON is intended to be human-readable and portable. Paths are stored exactly
as entered. At runtime, ~ expands to the user's home directory, and relative
paths resolve from the Python process's current working directory—not from the
directory containing the workflow JSON file. Use absolute paths or configure
paths with runtime overrides when running the same workflow on another machine.
Execute a workflow from a Python script¶
The UI is optional: saved workflows can be executed headlessly from a script.
Install FlowGraph in the environment where the script runs, then call
execute_workflow_file(). The parameter_overrides mapping is keyed by node
instance ID, so it changes inputs for this run without modifying the JSON file.
This example uses the packaged SimpleReadMesh.json demo workflow. Replace
workflow_path and mesh_path with paths to your own workflow and mesh:
from pathlib import Path
from flowgraph.application.workflow_io import execute_workflow_file
workflow_path = Path("src/flowgraph/testdata/SimpleReadMesh.json")
mesh_path = Path("data/input.stl").resolve()
result = execute_workflow_file(
workflow_path,
parameter_overrides={
"file-path": {"path": str(mesh_path)},
},
)
print("Executed nodes:", result.execution_order)
# Outputs are addressed by node ID and output-port name.
mesh_document = result.node_outputs["load-muscat"]["mesh"]
print("Loaded:", type(mesh_document).__name__)
# A sink has no output, so inspect the value delivered to its input instead.
mesh_for_view = result.node_inputs["mesh-sink_2"]["mesh"]
assert mesh_for_view is mesh_document
Workflow inputs and outputs¶
A Python-built workflow can publish a node input (including a parameter port) as a workflow input, and publish node outputs as workflow outputs. Exported inputs must remain unconnected inside the graph; callers provide their values when executing the workflow. The workflow then behaves like a node:
workflow.add_input("text", "transform", "value", registry)
workflow.add_output("result", "transform", "result", registry)
outputs = workflow.execute({"text": "hello"}, registry)
# The same interface is available as workflow.executor(registry).
The public interface is included when the workflow is saved, so it survives a JSON round trip.
A workflow can also be executed inside another workflow with Run Full Workflow. Expand the node and click Edit workflow to replace the current Workflow view with the nested graph. Build the nested graph normally, using Workflow Input and Workflow Output nodes to define its public interface. Click Quit workflow at the top of the Workflow view to return to the parent graph.
The node exposes typed dynamic ports matching the nested workflow's published inputs
and outputs: connect values to the published input names and use published output
names downstream. It also emits the nested WorkflowRunResult, including execution
order and per-node inputs and outputs, on result. Leaving a nested editor refreshes
its parent node's ports and invalidates that node and all downstream parent nodes.
Published interface types are inferred automatically. A Workflow Output exports the type of its connected upstream value. A Workflow Input exports a type that can safely feed every connected internal destination, including registered conversions. If the boundary is unconnected or its destinations have no common safe type, it remains Any. This lets parent workflows reject incompatible connections while retaining a flexible interface when the internal graph does not determine one type.
execute_workflow_file() returns a WorkflowRunResult. Its
node_outputs and node_inputs mappings retain the values produced and
consumed by each node. Runtime overrides are not written back to the workflow
file. To use a custom node catalog, create a NodeRegistry and pass it through
the keyword-only registry argument.
See Create and Run Python Nodes for a runnable example of
defining a node, registering it alongside the built-ins, and wiring it into a
Python workflow.
For a workflow that is already built in Python, use execute_workflow(workflow,
registry) instead. The Workflow Nodes reference
explains definitions, ports, parameters, validation, and output contracts.