2025-06-05T15:00:00Z
Following up on the earlier topic of multi-agent process workflows, at this session we will talk about the latest developments in running workflows that are defined via JSON and executed at runtime, while still having all the soundness and error handling benefits that Rust is known for.
We have defined a human-readable JSON schema for what we call “workflow diagrams”, i.e. workflows that can be represented as graphical diagrams. We also provide a library that allows these diagrams to be read, built into functional workflows, and executed… all at runtime! This JSON schema is designed to be as simple and intuitive as possible to allow commercial products to be built around it.
While assembling a workflow, the workflow builder will identify critical issues, like passing incompatible messages between operations, failing to terminate the workflow, referencing an operation that you forgot to define, or configuring an operation incorrectly. When these errors are present in the JSON diagram, the workflow builder will report on them with explicit error messages instead of allowing the workflow to be run.
To customize the behavior of a workflow, you can register your own “node builder” or “section builder” with the workflow builder. These element builders are written in Rust (which also allows binding to other languages like C++ and Python) and therefore have access to the full native workflow building API of bevy_impulse
along with any other dependencies you choose to use. Node and section builders can specify their own custom JSON-compatible config
data that allows the JSON diagram author to configure them.
While a “node” represents a unitary operation within a workflow, a “section” can encapsulate any number of workflow elements with any connection to each other. When you author a section you get to explicitly define what inputs, outputs, and buffers are exposed for elements outside of the section to connect to. These exposed elements form an “API” for the section. A common challenge with visual programming is the rapid inflation of tangled connections between countless operations. Many visual programming frameworks support hierarchy, allowing one isolated operation within a process to be defined via its own entire process, but sections are more flexible than this: Sections allow any set of inter-connected operations to connect to any other set of operations in any way that you choose.
Besides nodes and sections, there are many JSON diagram operations available out-of-the-box that will help you structure the flow of your data:
-
Stream: While a node is running, stream messages out that can activate other branches in the workflow. Useful for representing publishers or actions.
-
Fork Clone: Clone a message and send it down two different branches simultaneously. Useful for triggering parallel acitivities.
-
Fork Split: Split a message into a separate message for each field and send message down a different branch in the workflow.
-
Fork Result: For messages that have “Ok” and “Error” variants, send the Ok results down one branch, and send the Error results down another branch. This is useful for conditional branching and error handling.
-
Transform: Run a CEL script to transform a message into a new message.
-
Buffer: Store messages for later use
-
Join: For a specific set of buffers, wait until each buffer has at least one message and then pull one message from each buffer, combining them into a single message. This is useful for synchronizing parallel branches.
-
Listen: Run a branch each time a change occurs in one or more buffers.
-
Scope: Define a self-contained mini-workflow within the parent workflow. Useful for racing multiple branches or maintaining good state hygiene between multiple simultaneous threads of activity.
-
Section Template: Define a “section” of a workflow as a reusable JSON diagram template.
The native workflow building API provides even more operations that are gradually becoming available as JSON diagram operations, such as:
-
Spread: Turn a single collection of messages into a series of parallel-running messages. Useful for doing batch jobs in parallel.
-
Collect: Gather up messages until all upstream activity has finished, then send all the messages as a collection in a single message. Useful for synchornizing batch jobs.
-
Trim: Tell specific nodes within a workflow to cancel.
-
Gate Close/Open: Temporarily block messages along one branch.
-
Cleanup: Run a workflow to clean up the contents of one or more buffers when a workflow is finished.
-
Injection: Select a service at runtime and then execute it as if it is a regular node in the workflow.
These native operations can already be used by custom section builders.