VisualFlow is the portable workflow document produced by the visual editor in web/frontend/ and persisted by the backend in web/backend/.
The schema lives in ../abstractflow/visual/models.py (Pydantic models). Any host can:
- load/validate the JSON into
VisualFlow - execute it using
abstractflow.visualhelpers
See also: ../README.md, getting-started.md, faq.md, web-editor.md, architecture.md.
VisualFlowid: strname: str,description: strinterfaces: list[str](optional host contracts)nodes: list[VisualNode],edges: list[VisualEdge]entryNode: str | null(optional, Blueprint-style execution root)
VisualNodeid: str,type: NodeType,position: {x,y}data: dict(node config + pin metadata)
VisualEdgesource,sourceHandle,target,targetHandle
Evidence: ../abstractflow/visual/models.py.
- The full list of node types is
NodeTypein ../abstractflow/visual/models.py. - Pin types are
PinTypein ../abstractflow/visual/models.py (and mirrored for UI concerns in ../web/frontend/src/types/flow.ts).
Two edge “kinds” are used by convention:
- Execution edges: connect to the target handle
exec-in(Blueprint-style control flow). - Data edges: connect non-exec handles and carry values between pins.
Evidence:
- VisualFlow runner wiring uses execution-graph reachability (
targetHandle == "exec-in") in ../abstractflow/visual/executor.py. - UI colors data edges by pin type in ../web/frontend/src/components/Canvas.tsx.
Note on pins in saved JSON:
- The editor persists pin definitions under
node.data.inputs/node.data.outputs. - The top-level
node.inputs/node.outputsfields may be present but empty.
Evidence: ../abstractflow/visual/interfaces.py (_pin_types reads node.data.*) and sample flows in ../web/flows/.
Current media authoring nodes are saved as native VisualFlow node types:
generate_imageedit_imageimage_to_image(legacy alias normalized by the editor)generate_videotext_to_videoimage_to_videogenerate_voicegenerate_musictranscribe_audiolisten_voice
Generated image, video, voice, and music outputs should be treated as artifacts.
Gateway is the preferred execution host for these nodes because it owns media
catalogs, progress events, child runs, artifact persistence, and provider/runtime
configuration. Video nodes use scoped provider/model fields (video_provider,
video_model) and Gateway task catalogs (text_to_video, image_to_video) so
the graph does not rely on hardcoded model names.
Compatibility note: old saved flows that used the temporary browser-side
Generate Music lowering are normalized back to native generate_music when they
are loaded or saved by the current editor.
Subflows are regular VisualFlows referenced by id from a node of type subflow.
Convention:
node.type == "subflow"node.data["subflowId"]holds the referenced flow id (legacy keyflowIdis tolerated).
Evidence:
- Runner wiring resolves subflows in ../abstractflow/visual/executor.py (
subflowId/ legacyflowId) - Bundle packing is delegated to AbstractRuntime via ../abstractflow/workflow_bundle.py (see ../tests/test_workflow_bundle_pack.py).
- Tests: ../tests/test_visual_subflow_registry_reachability.py, ../tests/test_visual_subflow_recursion.py
VisualFlow.interfaces is a list of interface markers a host can interpret as “this workflow supports a known IO contract”.
AbstractFlow ships:
abstractcode.agent.v1(ABSTRACTCODE_AGENT_V1) with validators and scaffolding helpers
Evidence: ../abstractflow/visual/interfaces.py.