FlowNode

A FlowNode incorporates a subflow into an enclosing Flow as a single graph component. Its subflow field identifies the Flow definition that the runtime executes when the node runs. The FlowNode defines the input, output, and branch contract through which the enclosing Flow interacts with the subflow.

The subflow's internal nodes and connections remain separate from the enclosing graph. This boundary allows a Flow definition to be reused in different workflows without changing its internal structure.

When the FlowNode executes, the runtime maps its input values to the subflow's input contract and begins the subflow at its StartNode. The subflow executes until it reaches an EndNode, and the resulting values are mapped to the FlowNode outputs.

Description of the illustration FlowNode Nested Subflow Execution

The enclosing Flow interacts with the subflow only through the FlowNode. Its edges do not connect directly to the nodes inside order-lookup-flow.

The two control graphs are separate. Control reaches the FlowNode in the enclosing Flow, and the FlowNode starts the subflow at the subflow's own StartNode. After the subflow reaches its EndNode, the enclosing Flow can continue from the FlowNode.

Subflow Completion and Branch Selection

A subflow can contain multiple EndNode components for different completion paths. The branch_name of the EndNode reached during execution becomes the branch selected by the enclosing FlowNode.

For example:

The FlowNode declares eligible and ineligible in its branches field. Outgoing control-flow edges in the enclosing Flow use the same names in from_branch to describe the possible continuation.

The enclosing Flow connects to the FlowNode, not directly to the EndNodes inside the subflow. Values returned by either EndNode must satisfy the subflow and FlowNode output contracts.

FlowNode Fields

FieldRequirementPurpose
component_typeRequiredIdentifies the component as a FlowNode.
idRequiredUniquely identifies the node for references and graph connections.
nameRequiredProvides a human-readable component name.
subflowRequiredIdentifies the Flow executed by the node, directly or through a component reference.
inputsOptionalDeclares the typed values supplied to the subflow. When the subflow has inputs, the FlowNode must declare a compatible input contract with the same names and number of entries.
outputsOptionalDeclares the typed values returned from the subflow. When the subflow has outputs, the FlowNode must declare a compatible output contract with the same names and number of entries.
branchesOptionalDeclares the branch names available from the FlowNode. An outgoing control-flow edge can associate each name with a possible next node.

The enclosing Flow and the subflow remain separate control-flow graphs. Connections in the enclosing Flow refer to the FlowNode, not to nodes inside the subflow. Connections within the subflow refer only to nodes in that subflow.

The runtime applies the following rules to the FlowNode and subflow contracts:

  • The FlowNode and subflow must declare the same number of inputs.
  • Every FlowNode input must have a subflow input with the same case-sensitive name.
  • The FlowNode input type must be compatible with the corresponding subflow input type, so every value accepted through the node can be supplied to the subflow.
  • The subflow and FlowNode must declare the same number of outputs.
  • Every subflow output must have a FlowNode output with the same case-sensitive name.
  • The subflow output type must be compatible with the corresponding FlowNode output type, so every value produced by the subflow can be returned through the node.

FlowNode Example

A support Flow needs to retrieve the status of an order. An existing order-lookup-flow already defines this workflow with the following public
contract:

DirectionNameType
Inputorder_idstring
Outputorder_statusstring

The support Flow incorporates this subflow through a FlowNode. The enclosing Flow interacts only with the FlowNode contract and does not refer directly to nodes inside the subflow.

Description of the illustration FlowNode and Subflow Boundary

The FlowNode is defined as follows:

{
  "component_type": "FlowNode",
  "id": "run-order-lookup",
  "name": "run_order_lookup",
  "inputs": [
    {
      "title": "order_id",
      "type": "string"
    }
  ],
  "outputs": [
    {
      "title": "order_status",
      "type": "string"
    }
  ],
  "branches": ["next"],
  "subflow": {
    "$component_ref": "order-lookup-flow"
  }
}

Referenced Subflow

The subflow reference resolves to a separate Flow whose id is order-lookup-flow. Its public input and output contracts match the corresponding FlowNode contracts:

{
  "component_type": "Flow",
  "id": "order-lookup-flow",
  "name": "order_lookup_flow",
  "inputs": [
    {
      "title": "order_id",
      "type": "string"
    }
  ],
  "outputs": [
    {
      "title": "order_status",
      "type": "string"
    }
  ],
  "start_node": {
    "$component_ref": "order-lookup-start"
  },
  "nodes": [
    {"$component_ref": "order-lookup-start"},
    {"$component_ref": "lookup-order"},
    {"$component_ref": "order-lookup-end"}
  ],
  "control_flow_connections": [
    {
      "component_type": "ControlFlowEdge",
      "id": "order-lookup-start-to-tool",
      "name": "order_lookup_start_to_tool",
      "from_node": {
        "$component_ref": "order-lookup-start"
      },
      "from_branch": null,
      "to_node": {
        "$component_ref": "lookup-order"
      }
    },
    {
      "component_type": "ControlFlowEdge",
      "id": "order-lookup-tool-to-end",
      "name": "order_lookup_tool_to_end",
      "from_node": {
        "$component_ref": "lookup-order"
      },
      "from_branch": null,
      "to_node": {
        "$component_ref": "order-lookup-end"
      }
    }
  ],
  "data_flow_connections": [
    {
      "component_type": "DataFlowEdge",
      "id": "order-id-to-lookup-tool",
      "name": "order_id_to_lookup_tool",
      "source_node": {
        "$component_ref": "order-lookup-start"
      },
      "source_output": "order_id",
      "destination_node": {
        "$component_ref": "lookup-order"
      },
      "destination_input": "order_id"
    },
    {
      "component_type": "DataFlowEdge",
      "id": "order-status-to-lookup-end",
      "name": "order_status_to_lookup_end",
      "source_node": {
        "$component_ref": "lookup-order"
      },
      "source_output": "order_status",
      "destination_node": {
        "$component_ref": "order-lookup-end"
      },
      "destination_input": "order_status"
    }
  ],
  "$referenced_components": {
    "order-lookup-start": {
      "component_type": "StartNode",
      "id": "order-lookup-start",
      "name": "order_lookup_start",
      "inputs": [
        {
          "title": "order_id",
          "type": "string"
        }
      ],
      "outputs": [
        {
          "title": "order_id",
          "type": "string"
        }
      ],
      "branches": ["next"]
    },
    "lookup-order": {
      "component_type": "ToolNode",
      "id": "lookup-order",
      "name": "lookup_order",
      "inputs": [
        {
          "title": "order_id",
          "type": "string"
        }
      ],
      "outputs": [
        {
          "title": "order_status",
          "type": "string"
        }
      ],
      "branches": ["next"],
      "tool": {
        "$component_ref": "lookup-orders-tool"
      }
    },
    "order-lookup-end": {
      "component_type": "EndNode",
      "id": "order-lookup-end",
      "name": "order_lookup_end",
      "inputs": [
        {
          "title": "order_status",
          "type": "string"
        }
      ],
      "outputs": [
        {
          "title": "order_status",
          "type": "string"
        }
      ],
      "branches": [],
      "branch_name": "next"
    },
    "lookup-orders-tool": {
      "component_type": "BuiltinTool",
      "id": "lookup-orders-tool",
      "name": "lookup_orders",
      "executor_name": "Oracle In-DB Agent Executor",
      "tool_type": "plsql_exec",
      "configuration": {
        "schema_name": "APP_SCHEMA",
        "package_name": "ORDER_TOOLS",
        "procedure_name": "LOOKUP_ORDER"
      }
    }
  }
}

The subflow definition establishes the complete reusable workflow:

  • id matches the $component_ref in the FlowNode.
  • inputs declares the order_id value accepted from the FlowNode.
  • outputs declares the order_status value returned to the FlowNode.
  • start_node identifies order-lookup-start as the first node in the subflow.
  • control_flow_connections describes the possible execution sequence from StartNode to ToolNode to EndNode.
  • data_flow_connections transfers order_id to the ToolNode and transfers order_status to the EndNode.
  • $referenced_components contains the node and Tool definitions resolved by the component references in the subflow.

The StartNode exposes the subflow input as order_id. The ToolNode invokes APP_SCHEMA.ORDER_TOOLS.LOOKUP_ORDER, and the EndNode maps the resulting order_status value to the subflow output contract. Its branch_name value, next, becomes the branch selected by the enclosing FlowNode when the subflow completes.

The fields establish the boundary between the enclosing Flow and the subflow:

  • inputs declares the order_id value supplied to the subflow.
  • outputs declares the order_status value returned from the subflow.
  • branches declares the next branch that an outgoing control-flow edge can reference.
  • subflow identifies the Flow executed by the node. In this example, its $component_ref resolves to the Flow whose ID is order-lookup-flow.

When the FlowNode executes:

  1. The runtime maps FlowNode.order_id to the order_id input of order-lookup-flow.
  2. The subflow begins at its own StartNode and executes its internal graph.
  3. The subflow reaches an EndNode with an order_status result.
  4. The runtime maps the subflow's order_status output to FlowNode.order_status.
  5. The enclosing Flow can transfer FlowNode.order_status to another node through a data-flow edge.

Complete Runnable Agent Spec

The following example executes order-lookup-flow through a FlowNode. It accepts an order_id, invokes a PL/SQL procedure inside the subflow, and returns order_status through the enclosing Flow.

This example does not require an LLM configuration, credential, or network ACL. The application schema must be able to create the example package and execute DBMS_AI_AGENT.

Create the Example Tool

Connect as the application schema and create the following package:

CREATE OR REPLACE PACKAGE ORDER_TOOLS AS
  PROCEDURE LOOKUP_ORDER (
    ORDER_ID     IN  VARCHAR2,
    ORDER_STATUS OUT VARCHAR2
  );
END ORDER_TOOLS;
/

CREATE OR REPLACE PACKAGE BODY ORDER_TOOLS AS
  PROCEDURE LOOKUP_ORDER (
    ORDER_ID     IN  VARCHAR2,
    ORDER_STATUS OUT VARCHAR2
  ) IS
  BEGIN
    IF ORDER_ID = '7812' THEN
      ORDER_STATUS := 'OUT_FOR_DELIVERY';
    ELSE
      ORDER_STATUS := 'PROCESSING';
    END IF;
  END LOOKUP_ORDER;
END ORDER_TOOLS;
/

Execute the Complete Agent Spec

SET SERVEROUTPUT ON SIZE UNLIMITED
SET DEFINE OFF

DECLARE
  L_AGENT_SPEC CLOB := q'~
{
  "component_type": "Flow",
  "agentspec_version": "25.4.1",
  "id": "support-flow",
  "name": "support_flow",
  "inputs": [
    {
      "title": "order_id",
      "type": "string"
    }
  ],
  "outputs": [
    {
      "title": "order_status",
      "type": "string"
    }
  ],
  "start_node": {
    "$component_ref": "support-start"
  },
  "nodes": [
    {
      "$component_ref": "support-start"
    },
    {
      "$component_ref": "run-order-lookup"
    },
    {
      "$component_ref": "support-end"
    }
  ],
  "control_flow_connections": [
    {
      "component_type": "ControlFlowEdge",
      "id": "support-start-to-lookup",
      "name": "support_start_to_lookup",
      "from_node": {
        "$component_ref": "support-start"
      },
      "to_node": {
        "$component_ref": "run-order-lookup"
      }
    },
    {
      "component_type": "ControlFlowEdge",
      "id": "lookup-to-support-end",
      "name": "lookup_to_support_end",
      "from_node": {
        "$component_ref": "run-order-lookup"
      },
      "from_branch": "next",
      "to_node": {
        "$component_ref": "support-end"
      }
    }
  ],
  "data_flow_connections": [
    {
      "component_type": "DataFlowEdge",
      "id": "outer-order-id-to-flow-node",
      "name": "outer_order_id_to_flow_node",
      "source_node": {
        "$component_ref": "support-start"
      },
      "source_output": "order_id",
      "destination_node": {
        "$component_ref": "run-order-lookup"
      },
      "destination_input": "order_id"
    },
    {
      "component_type": "DataFlowEdge",
      "id": "flow-node-status-to-outer-end",
      "name": "flow_node_status_to_outer_end",
      "source_node": {
        "$component_ref": "run-order-lookup"
      },
      "source_output": "order_status",
      "destination_node": {
        "$component_ref": "support-end"
      },
      "destination_input": "order_status"
    }
  ],
  "$referenced_components": {
    "support-start": {
      "component_type": "StartNode",
      "id": "support-start",
      "name": "support_start",
      "inputs": [
        {
          "title": "order_id",
          "type": "string"
        }
      ],
      "outputs": [
        {
          "title": "order_id",
          "type": "string"
        }
      ],
      "branches": [
        "next"
      ]
    },
    "run-order-lookup": {
      "component_type": "FlowNode",
      "id": "run-order-lookup",
      "name": "run_order_lookup",
      "inputs": [
        {
          "title": "order_id",
          "type": "string"
        }
      ],
      "outputs": [
        {
          "title": "order_status",
          "type": "string"
        }
      ],
      "branches": [
        "next"
      ],
      "subflow": {
        "$component_ref": "order-lookup-flow"
      }
    },
    "support-end": {
      "component_type": "EndNode",
      "id": "support-end",
      "name": "support_end",
      "inputs": [
        {
          "title": "order_status",
          "type": "string"
        }
      ],
      "outputs": [
        {
          "title": "order_status",
          "type": "string"
        }
      ],
      "branches": [],
      "branch_name": "next"
    },
    "order-lookup-flow": {
      "component_type": "Flow",
      "id": "order-lookup-flow",
      "name": "order_lookup_flow",
      "inputs": [
        {
          "title": "order_id",
          "type": "string"
        }
      ],
      "outputs": [
        {
          "title": "order_status",
          "type": "string"
        }
      ],
      "start_node": {
        "$component_ref": "order-lookup-start"
      },
      "nodes": [
        {
          "$component_ref": "order-lookup-start"
        },
        {
          "$component_ref": "lookup-order"
        },
        {
          "$component_ref": "order-lookup-end"
        }
      ],
      "control_flow_connections": [
        {
          "component_type": "ControlFlowEdge",
          "id": "order-lookup-start-to-tool",
          "name": "order_lookup_start_to_tool",
          "from_node": {
            "$component_ref": "order-lookup-start"
          },
          "to_node": {
            "$component_ref": "lookup-order"
          }
        },
        {
          "component_type": "ControlFlowEdge",
          "id": "order-lookup-tool-to-end",
          "name": "order_lookup_tool_to_end",
          "from_node": {
            "$component_ref": "lookup-order"
          },
          "to_node": {
            "$component_ref": "order-lookup-end"
          }
        }
      ],
      "data_flow_connections": [
        {
          "component_type": "DataFlowEdge",
          "id": "order-id-to-lookup-tool",
          "name": "order_id_to_lookup_tool",
          "source_node": {
            "$component_ref": "order-lookup-start"
          },
          "source_output": "order_id",
          "destination_node": {
            "$component_ref": "lookup-order"
          },
          "destination_input": "order_id"
        },
        {
          "component_type": "DataFlowEdge",
          "id": "order-status-to-lookup-end",
          "name": "order_status_to_lookup_end",
          "source_node": {
            "$component_ref": "lookup-order"
          },
          "source_output": "order_status",
          "destination_node": {
            "$component_ref": "order-lookup-end"
          },
          "destination_input": "order_status"
        }
      ],
      "$referenced_components": {
        "order-lookup-start": {
          "component_type": "StartNode",
          "id": "order-lookup-start",
          "name": "order_lookup_start",
          "inputs": [
            {
              "title": "order_id",
              "type": "string"
            }
          ],
          "outputs": [
            {
              "title": "order_id",
              "type": "string"
            }
          ],
          "branches": [
            "next"
          ]
        },
        "lookup-order": {
          "component_type": "ToolNode",
          "id": "lookup-order",
          "name": "lookup_order",
          "inputs": [
            {
              "title": "order_id",
              "type": "string"
            }
          ],
          "outputs": [
            {
              "title": "order_status",
              "type": "string"
            }
          ],
          "branches": [
            "next"
          ],
          "tool": {
            "$component_ref": "lookup-orders-tool"
          }
        },
        "order-lookup-end": {
          "component_type": "EndNode",
          "id": "order-lookup-end",
          "name": "order_lookup_end",
          "inputs": [
            {
              "title": "order_status",
              "type": "string"
            }
          ],
          "outputs": [
            {
              "title": "order_status",
              "type": "string"
            }
          ],
          "branches": [],
          "branch_name": "next"
        },
        "lookup-orders-tool": {
          "component_type": "BuiltinTool",
          "id": "lookup-orders-tool",
          "name": "lookup_orders",
          "description": "Return the current status of an order.",
          "inputs": [
            {
              "title": "order_id",
              "type": "string"
            }
          ],
          "outputs": [
            {
              "title": "order_status",
              "type": "string"
            }
          ],
          "requires_confirmation": false,
          "executor_name": "Oracle In-DB Agent Executor",
          "tool_version": "1.0",
          "tool_type": "plsql_exec",
          "configuration": {
            "package_name": "ORDER_TOOLS",
            "procedure_name": "LOOKUP_ORDER"
          }
        }
      }
    }
  }
}
~';

  L_INPUTS CLOB := TO_CLOB (
    '[{"name":"order_id","value":"7812"}]'
  );
  L_OUTPUTS CLOB;
BEGIN
  L_OUTPUTS := DBMS_AI_AGENT.EXECUTE_IMMEDIATE (
    AGENT_SPEC => L_AGENT_SPEC,
    INPUTS     => L_INPUTS
  );

  DBMS_OUTPUT.PUT_LINE (
    DBMS_LOB.SUBSTR (L_OUTPUTS, 32767, 1)
  );
END;
/

The returned JSON has the following form:

{
  "order_status": "OUT_FOR_DELIVERY"
}

In this execution:

  1. The enclosing StartNode publishes order_id.
  2. The outer data-flow edge supplies the value to FlowNode.order_id.
  3. The FlowNode maps the value to the order_id input of order-lookup-flow.
  4. The subflow starts at order-lookup-start and supplies the value to lookup-order.
  5. The ToolNode invokes ORDER_TOOLS.LOOKUP_ORDER.
  6. The subflow transfers order_status to order-lookup-end.
  7. The subflow output becomes FlowNode.order_status.
  8. The enclosing Flow transfers that value to support-end and returns it through its order_status output.