> ## Documentation Index
> Fetch the complete documentation index at: https://docs.inworld.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Graph Editor

## Overview

The **Inworld Graph Editor** (part of the **InworldRuntime** Unreal plugin) enables developers to build their graphs in Unreal using an easy-to-use visual editor. It lets you build graphs where **nodes** or **subgraphs** are connected by **edges**. Nodes/Subgraphs process data; edges define execution flow. Graphs are saved as assets and can be executed at runtime.

## Graph workflow summary

<Steps titleSize="h3">
  <Step title="Create a Graph asset">
    From the Content Browser context menu.
  </Step>

  <Step title="Design Graph">
    Open the editor, add nodes, subgraphs, configure nodes, connect edges.
  </Step>

  <Step title="Mark Start/End">
    Allows to have multiple Start and End nodes.
  </Step>

  <Step title="Runtime: Get Graph instance">
    Get graph instance using graph asset.
  </Step>

  <Step title="Runtime: Execute">
    Call Execute, pass Input and RuntimeData if needed.
  </Step>

  <Step title="Runtime: Handle Result">
    Handle result callbacks from End nodes
  </Step>
</Steps>

## Editor Layout

<img src="https://mintcdn.com/inworldai/w_7yLhk0qAMORJ2N/img/unreal/graph-editor/editor_layout.png?fit=max&auto=format&n=w_7yLhk0qAMORJ2N&q=85&s=e2fadef9034f15004f49db543b34f7e1" alt="" width="1405" height="956" data-path="img/unreal/graph-editor/editor_layout.png" />

1. **Canvas** -- place and connect nodes. Drag to move. **Ctrl+C/Ctrl+V** to copy/paste. *Del* to delete node/edge.
2. **Details Panel (right)** -- properties of the selected node/edge.
3. **Graph Settings (below Details)** -- shows the description, execution mode and list of validation messages.
   **Execution mode** defines how multiple executions are handled. Three modes are available::
   * Simultaneous: Allow multiple executions to run simultaneously.
   * Latest Only: Cancels previous execution when starting a new one.
   * Sequential: Queues new executions until the current one finishes (FIFO), no cancellations.
     <img src="https://mintcdn.com/inworldai/w_7yLhk0qAMORJ2N/img/unreal/graph-editor/editor_contextmenu_nodes.png?fit=max&auto=format&n=w_7yLhk0qAMORJ2N&q=85&s=b54274c664a4525d0645e3fdf65f3ec5" alt="" width="732" height="530" data-path="img/unreal/graph-editor/editor_contextmenu_nodes.png" />

* **Context Menu (right-click on canvas)** -- to add nodes, comment or subgraphs or on **edge icon** to define edge type.
  * Start typing to **filter** by name.
  * Hover a menu item to see its description, inputs/outputs.

<Tip>
  **Tooltips:** Hover an **input/output pin** to see its data type in a tooltip.

  <img src="https://mintcdn.com/inworldai/w_7yLhk0qAMORJ2N/img/unreal/graph-editor/tip_pintooltip.png?fit=max&auto=format&n=w_7yLhk0qAMORJ2N&q=85&s=8e68d438e1784867388dc43e7054690f" alt="" width="413" height="143" data-path="img/unreal/graph-editor/tip_pintooltip.png" />
</Tip>

## Creating a Graph Asset

1. **Add New > Inworld > Graph**.
2. Name and save the asset > Double-click to open Inworld Graph Editor.
   <img src="https://mintcdn.com/inworldai/w_7yLhk0qAMORJ2N/img/unreal/graph-editor/create_graphasset.png?fit=max&auto=format&n=w_7yLhk0qAMORJ2N&q=85&s=b7e1a82d5cc594b55972ee779fc589fe" alt="" width="1408" height="963" data-path="img/unreal/graph-editor/create_graphasset.png" />

## Nodes

* **Adding**:
  * right-click on the canvas and select a node from the menu.
  * drag from a pin and release to open node selection menu. In this case, the menu is filtered to show only nodes that can accept the input of the dragged pin. (can be changed in the settings)
* **Move/Copy/Paste/Delete**: drag to move; **Ctrl+C/Ctrl+V** to copy/paste; select node *Del* to delete.
* **Hover for Info**: hover a node to see description and pins info.
  <img src="https://mintcdn.com/inworldai/w_7yLhk0qAMORJ2N/img/unreal/graph-editor/node_tooltip.png?fit=max&auto=format&n=w_7yLhk0qAMORJ2N&q=85&s=8534293601e8da1901e7ef91703ee60d" alt="" width="635" height="302" data-path="img/unreal/graph-editor/node_tooltip.png" />
* **Pins & Types**:
  * Each input/output pin has a **data type** (pins are color-coded).
  * Optional inputs are indicated by \* and noted in the tooltip.
  * Type checks prevent invalid connections; behavior configurable in *Settings*.
* **Pin Display Modes** (Settings): label + color, color only, or label only. Labels can show **Pin Name** or **Data Type**.
* **Double-click node body** > opens node implementation for custom nodes or subgraphs.

<Tabs>
  <Tab title="Blueprint">
    <img src="https://mintcdn.com/inworldai/w_7yLhk0qAMORJ2N/img/unreal/graph-editor/doubleclick_bpnode.png?fit=max&auto=format&n=w_7yLhk0qAMORJ2N&q=85&s=4f2c21ba6e778942320c92e5381c11fd" alt="" width="1551" height="977" data-path="img/unreal/graph-editor/doubleclick_bpnode.png" />
  </Tab>

  <Tab title="C++">
    Opens the source file at the line of node implementation.

    <img src="https://mintcdn.com/inworldai/UryOiaYxRIIFAVEV/img/unreal/graph-editor/doubleclick_cppnode.png?fit=max&auto=format&n=UryOiaYxRIIFAVEV&q=85&s=6e4e1991a923d0e4da7c461da04d1b0e" alt="" width="1310" height="970" data-path="img/unreal/graph-editor/doubleclick_cppnode.png" />
  </Tab>
</Tabs>

* **Double-click node name** > **rename** the node inline.
* **Getter Nodes**: set IsGetter to true if no input required for node. In this case, you do not need to set up an input connection for the node. (optional you can wire input for explicit ordering).

### Custom Node

Custom nodes can be implemented using Blueprint or C++. In this example, we will implement a custom node that processes PlayerProfile RuntimeData and two Text inputs to combine them into one Text output.

<Tabs>
  <Tab title="Blueprint">
    1. **Create Custom Node**

       Create via *Content Browser*  or the **New Custom Node** button in the Graph Editor.
       Custom nodes implemented in Blueprints have an icon <img src="https://mintcdn.com/inworldai/KIcGsHsiYIN4yx-P/img/unreal/graph-editor/bp_customnode_icon.png?fit=max&auto=format&n=KIcGsHsiYIN4yx-P&q=85&s=ba6dc13c264b5e4c570537eae6bdb9a4" alt="" width="50" height="43" data-path="img/unreal/graph-editor/bp_customnode_icon.png" /> in Graph Editor.

    2. **Class Defaults**

       Define:

       * Node Name
       * Node Description (Description of the node functionality and purpose)
       * Is Getter (Special getter node mode that not required for input)
             <img src="https://mintcdn.com/inworldai/w_7yLhk0qAMORJ2N/img/unreal/graph-editor/bp_customnode_impl_def.png?fit=max&auto=format&n=w_7yLhk0qAMORJ2N&q=85&s=363cd5b20a2d0043fbd35c7355c2ac78" alt="" width="1551" height="977" data-path="img/unreal/graph-editor/bp_customnode_impl_def.png" />

    3. **Process Function**

       To make a function available as a processing node, its name must start with **Process**.
       Examples: **`Process`**, **`ProcessDialogue`**, **`ProcessAudio`**, **`ProcessLogic`**.

       Each function defines the node's execution logic.
       The defined inputs and outputs (names and types) are automatically reflected as pins in the Graph Editor.

       A single node can be linked to multiple processing functions.
       When executing, the node automatically selects the appropriate function based on matching input types.

           <img src="https://mintcdn.com/inworldai/w_7yLhk0qAMORJ2N/img/unreal/graph-editor/bp_customnode_impl.png?fit=max&auto=format&n=w_7yLhk0qAMORJ2N&q=85&s=d0675b6dd1ebfbc4d4d406fab4d40381" alt="" width="1551" height="977" data-path="img/unreal/graph-editor/bp_customnode_impl.png" />

    4. **Save**

       The node appears in the context menu immediately.

           <img src="https://mintcdn.com/inworldai/w_7yLhk0qAMORJ2N/img/unreal/graph-editor/bp_customnode.png?fit=max&auto=format&n=w_7yLhk0qAMORJ2N&q=85&s=937bed11bbe15f35b38f949ff04a722c" alt="" width="466" height="294" data-path="img/unreal/graph-editor/bp_customnode.png" />
  </Tab>

  <Tab title="C++">
    1. **Create Custom Node class**

       Create a C++ class inheriting from UInworldNode\_Custom.
       Custom nodes that implemented in C++ have an icon <img src="https://mintcdn.com/inworldai/KIcGsHsiYIN4yx-P/img/unreal/graph-editor/cpp_customnode_icon.png?fit=max&auto=format&n=KIcGsHsiYIN4yx-P&q=85&s=94f4a50f193a209bfa5a0c91805e6ec8" alt="" width="50" height="48" data-path="img/unreal/graph-editor/cpp_customnode_icon.png" /> in Graph Editor.

    2. **Define at constructor**

       Define:

       * Node Name
       * IsGetter (special getter node that not required for input)

    3. **Process Function**

       To make a function available as a processing node:

       * Mark it with UFUNCTION()
       * Its name must start with **Process**.

       Examples: **`Process`**, **`ProcessDialogue`**, **`ProcessAudio`**, **`ProcessLogic`**.

       Each function defines the node's execution logic.
       The defined inputs and outputs (names and types) are automatically reflected as pins in the Graph Editor.

       A single node can be linked to multiple processing functions.
       When executing, the node automatically selects the appropriate function based on matching input types.

       You can also optionally define the process context as the first argument to access runtime information:

    ```cpp theme={"system"}
      FInworldData_Text Process(const UInworldProcessContext* ProcessContext, const FInworldData_Text& A, const FInworldData_Text& B) const
    ```

    The UInworldProcessContext parameter provides access to contextual data and runtime state during graph execution.
    If present, it will be automatically passed by the graph system.

    ```cpp theme={"system"}
    #pragma once

    #include "CoreMinimal.h"
    #include "Graph/Nodes/InworldNode_Custom.h"
    #include "MyInworldNode_AppendText.generated.h"

    /**
     * @class UInworldNode_GetCharacterName
     * @brief A node that append two text inputs.
     */
    UCLASS()
    class MYGAME_API UMyInworldNode_AppendText : public UInworldNode_Custom
    {
      GENERATED_BODY()

    public:
      UMyInworldNode_AppendText()
      {
        NodeName = "Append Text";
      }

    protected:
      UFUNCTION()
      UPARAM(meta = (DisplayName = "A + B")) 
      FInworldData_Text Process(const FInworldData_Text& A, const FInworldData_Text& B) const
      {
          if (A.Text.IsEmpty() || B.Text.IsEmpty())
          {
            return FInworldData_Text::Error(TEXT("A or B is empty."));
          }
          FInworldData_Text ResultData;
          ResultData.Text = A.Text + " " + B.Text;
          return ResultData;
      }
    };
    ```

    4. **Compile**
       The node appears in the context menu.
           <img src="https://mintcdn.com/inworldai/w_7yLhk0qAMORJ2N/img/unreal/graph-editor/cpp_customnode.png?fit=max&auto=format&n=w_7yLhk0qAMORJ2N&q=85&s=7e183895bdb2f78d26e944361aec03de" alt="" width="479" height="364" data-path="img/unreal/graph-editor/cpp_customnode.png" />
  </Tab>
</Tabs>

## Edges

* **Connect** by dragging between pins.
* **Delete**: select edge by click on icon > **Del**.
* **Input/Ouput Types Checking**:
  * **Disallow connection - checked** (default): incompatible types **cannot** connect.
    <img src="https://mintcdn.com/inworldai/w_7yLhk0qAMORJ2N/img/unreal/graph-editor/disallow_connection_checked.png?fit=max&auto=format&n=w_7yLhk0qAMORJ2N&q=85&s=bb3b318e2c65f93071b822d336aaedac" alt="" width="605" height="379" data-path="img/unreal/graph-editor/disallow_connection_checked.png" />
  * **Disallow connection - unchecked**: connection allowed but a **warning** is shown.
    <img src="https://mintcdn.com/inworldai/w_7yLhk0qAMORJ2N/img/unreal/graph-editor/disallow_connection_unchecked.png?fit=max&auto=format&n=w_7yLhk0qAMORJ2N&q=85&s=f49171a2458d3cce4c00ca0d9c3e3c6b" alt="" width="604" height="404" data-path="img/unreal/graph-editor/disallow_connection_unchecked.png" />
* **Conditional Edges**:
  * Set an edge type: right-click the edge > choose a **Condition type** (e.g., *General*, *IsSafe*).
    <img src="https://mintcdn.com/inworldai/UryOiaYxRIIFAVEV/img/unreal/graph-editor/editor_contextmenu_edges.png?fit=max&auto=format&n=UryOiaYxRIIFAVEV&q=85&s=e7856897798fdf33156422c682dc8429" alt="" width="565" height="601" data-path="img/unreal/graph-editor/editor_contextmenu_edges.png" />
  * Each condition can be **negated** at design time in the Details panel.
  * **IsSafe** is a special condition based edge on a safe-node result.
* **Edge Properties**: **Title**, **Negation**, **Required**.
  * Not Required edges are shown as **dashed** lines.
    <img src="https://mintcdn.com/inworldai/UryOiaYxRIIFAVEV/img/unreal/graph-editor/notrequired_edge.png?fit=max&auto=format&n=UryOiaYxRIIFAVEV&q=85&s=0e6ddc3ee111abf572c1d145735495e8" alt="" width="847" height="468" data-path="img/unreal/graph-editor/notrequired_edge.png" />
  * **To Open Implementation**: double-click on conditional edge widget to navigate to implementation (C++ or Blueprint).

### Custom Conditional Edge

Custom edges can be implemented using Blueprint or C++.

<Tabs>
  <Tab title="Blueprint">
    1. **Create Custom Edge**

       Create via *Content Browser* context menu or the **New Custom Edge** button in the Graph Editor.

    2. **Class Defaults**

       Define:

       * Edge Title
       * Execute Meets Condition in Game Thread

    3. **MeetsCondition Function**

       To make a function available as a condition, its name must start with **MeetsCondition**.
       Examples: **`MeetsCondition`**, **`MeetsConditionCheck`**, **`MeetsConditionIsValid`**, **`MeetsConditionLogic`**.

       Each function defines the edge's conditional logic and must return a bool value.
       A single edge can be linked to multiple condition functions.
       When executing, the edge automatically selects the appropriate function based on matching input type.

           <img src="https://mintcdn.com/inworldai/KIcGsHsiYIN4yx-P/img/unreal/graph-editor/bp_customconditionedge.png?fit=max&auto=format&n=KIcGsHsiYIN4yx-P&q=85&s=cd9685f067040effb4b1af2dd50050fd" alt="" width="1509" height="927" data-path="img/unreal/graph-editor/bp_customconditionedge.png" />

    4. **Save**

       The edge appears in the context menu immediately.
  </Tab>

  <Tab title="C++">
    1. **Create Custom Condition Edge class**

       Create a C++ class inheriting from UInworldEdge\_WithCondition.

    2. **At Constructor**

       Define:

       * Edge Title
       * Execute Meets Condition in Game Thread

    3. **MeetsCondition Function**

       To make a function available as a condition:

       * Mark it with UFUNCTION()
       * Its name must start with **MeetsCondition**
         Examples: **`MeetsCondition`**, **`MeetsConditionCheck`**, **`MeetsConditionIsValid`**, **`MeetsConditionLogic`**.

       Each function defines the edge's conditional logic and must return a bool value.

       A single edge can be linked to multiple condition functions.
       When executing, the edge automatically selects the appropriate function based on matching input type.

       You can also optionally define the process context as the first argument to access runtime information:

    ```cpp theme={"system"}
      bool MeetsCondition(const UInworldProcessContext* ProcessContext, 
                          const FInworldData_Text& Input) const
    ```

    The UInworldProcessContext parameter provides access to contextual data and runtime state during graph execution.
    If present, it will be automatically passed by the graph system.

    ```cpp theme={"system"}
    #pragma once

    #include "CoreMinimal.h"
    #include "Graph/Edges/InworldEdge_WithCondition.h"
    #include "MyInworldEdge_IsAudio.generated.h"

    UCLASS()
    class MYGAME_API UInworldEdge_MyEdge : public UInworldEdge_WithCondition
    {
      GENERATED_BODY()

    public:
      UInworldEdge_MyEdge()
      {
        EdgeTitle = "My Edge C++";
      }

    protected:
      UFUNCTION()
      bool MeetsCondition(const FInworldData_Text& Input) const
      {
        return (Input.Text.Contains(TEXT("Inworld")));
      }
    };

    ```

    4. **Compile**

       The node appears in the context menu.
  </Tab>
</Tabs>

## Start & End Nodes

* **Start Node**:
  * Mark a node as **Start** (right-click on node > Mark as Start).
  * At runtime, all **Start** nodes **receive the top-level input data** you pass into `Execute(...)` and RuntimeData.
  * A node with **no incoming edges** can be marked as a start point.
    <img src="https://mintcdn.com/inworldai/w_7yLhk0qAMORJ2N/img/unreal/graph-editor/node_as_start.png?fit=max&auto=format&n=w_7yLhk0qAMORJ2N&q=85&s=ed859121afe91ea4ae48510392881688" alt="" width="700" height="297" data-path="img/unreal/graph-editor/node_as_start.png" />
* **End Node**:
  * Mark a node **End** (right-click on node > Mark as End).
  * When an End node finishes, it emits a **result callback** carrying that node's output.
  * A node with **no outgoing edges** can be marked as an end point.
    <img src="https://mintcdn.com/inworldai/w_7yLhk0qAMORJ2N/img/unreal/graph-editor/node_as_end.png?fit=max&auto=format&n=w_7yLhk0qAMORJ2N&q=85&s=69ad31a1775de42e9ba0b3e2bb9fd050" alt="" width="700" height="297" data-path="img/unreal/graph-editor/node_as_end.png" />
* **Dual-role Node**: A node with **no inputs and outputs** can be marked as both **Start and End**.
  <img src="https://mintcdn.com/inworldai/w_7yLhk0qAMORJ2N/img/unreal/graph-editor/node_as_startend.png?fit=max&auto=format&n=w_7yLhk0qAMORJ2N&q=85&s=27b02a95b9eba461e639714fcdafa6ff" alt="" width="700" height="297" data-path="img/unreal/graph-editor/node_as_startend.png" />

## Subgraphs

Each saved graph asset that has exactly one Start and one End node can be added as a Subgraph to an existing graph as node.
The subgraph's inputs correspond to its Start node inputs, and its output comes from the End node.
All RuntimeData is automatically passed to the subgraph during execution.

* **Adding**:
  * Right-click on the canvas and select a subgraph from the `Subgraph` section of the context menu.
  * Or drag from a pin and release to open the selection menu.
    <img src="https://mintcdn.com/inworldai/w_7yLhk0qAMORJ2N/img/unreal/graph-editor/subgraph.png?fit=max&auto=format&n=w_7yLhk0qAMORJ2N&q=85&s=5c55b1f4c413dc0f33582b3986945a0f" alt="" width="496" height="317" data-path="img/unreal/graph-editor/subgraph.png" />

## Settings

<img src="https://mintcdn.com/inworldai/w_7yLhk0qAMORJ2N/img/unreal/graph-editor/grapheditor_settings.png?fit=max&auto=format&n=w_7yLhk0qAMORJ2N&q=85&s=e9e22731cc17752fe36a94c70f026c89" alt="" width="832" height="512" data-path="img/unreal/graph-editor/grapheditor_settings.png" />

## Executing a Graph using Graph Asset at Runtime

1. Get an instance: `GetGraphInstance`
2. Execute the graph: call **`Execute()`**.
   * **Start nodes** will receive `Input` and `RuntimeData` map.
   * Each **End node** triggers **`OnResultCallback`**  and returns the result along with the corresponding `NodeId` and `ExecutionId`.
3. Process results in your system.

<Tabs>
  <Tab title="Blueprint">
    <img src="https://mintcdn.com/inworldai/w_7yLhk0qAMORJ2N/img/unreal/graph-editor/bp_graph_execution.png?fit=max&auto=format&n=w_7yLhk0qAMORJ2N&q=85&s=aeffe549af3656bc2da185bd07942c49" alt="" width="1230" height="1087" data-path="img/unreal/graph-editor/bp_graph_execution.png" />
  </Tab>

  <Tab title="C++">
    ```cpp theme={"system"}
    #pragma once

    #include "CoreMinimal.h"
    #include "GameFramework/Actor.h"
    #include "Graph/InworldGraph.h"
    #include "Graph/Assets/InworldGraphAsset.h"
    #include "AGraphExecution.generated.h"

    UCLASS()
    class MYGAME_API AGraphExecution : public AActor
    {
      GENERATED_BODY()

    public:
      UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Graph")
      TObjectPtr<UInworldGraphAsset> GraphAsset;

    protected:
      virtual void BeginPlay() override
      {
        Super::BeginPlay();

        GraphAsset->GetGraphInstance(FOnGraphCompiledNative::CreateUObject(this, &AGraphExecution::OnGetGraph));
      };

      void OnGetGraph(UInworldGraph* CompiledGraph, bool bSuccess)
      {
        if (bSuccess)
        {
          FInworldData_Text InputData;
          InputData.Text = TEXT("Hi LLM");
          CompiledGraph->Execute(this, InputData, {}, FOnGraphResultNative::CreateUObject(this, &AGraphExecution::OnResult));
        }
      }

      void OnResult(const FString& ExecutionId, const FString& NodeId, const FInworldDataHandle& DataHandle)
      {
        if (FInworldData_Text* ResultTextData = DataHandle.Unwrap<FInworldData_Text>())
        {
          GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Green, ResultTextData->Text);
        }
      }
    };
    ```
  </Tab>
</Tabs>

## Inworld Data Handle

Graph input and result data must be of type **FInworldDataHandle**.

Input data must be "wrapped" into **FInworldDataHandle**.

<Tabs>
  <Tab title="Blueprint">
    <img src="https://mintcdn.com/inworldai/w_7yLhk0qAMORJ2N/img/unreal/graph-editor/data_wrap.png?fit=max&auto=format&n=w_7yLhk0qAMORJ2N&q=85&s=c47622921669c7e4fffba0d9e9537e52" alt="" width="502" height="126" data-path="img/unreal/graph-editor/data_wrap.png" />
  </Tab>

  <Tab title="C++">
    ```cpp theme={"system"}
    // FInworldData
    FInworldData_Text InworldText;
    FInworldDataHandle InworldDataHandle = InworldText;

    // Custom Struct Data
    FMyCustomStruct MyCustomStruct;
    FInworldDataHandle InworldDataHandle = FInworldData_Struct(MyCustomStruct);

    ```
  </Tab>
</Tabs>

Result data must be "unwrapped" out of **FInworldDataHandle**.

<Tabs>
  <Tab title="Blueprint">
    <img src="https://mintcdn.com/inworldai/w_7yLhk0qAMORJ2N/img/unreal/graph-editor/data_unwrap.png?fit=max&auto=format&n=w_7yLhk0qAMORJ2N&q=85&s=05e3b67fc106789f6d2cf1ecae6b1092" alt="" width="1009" height="220" data-path="img/unreal/graph-editor/data_unwrap.png" />
  </Tab>

  <Tab title="C++">
    ```cpp theme={"system"}
    // FInworldData
    if (FInworldData_Text* InworldText = InworldDataHandle->Unwrap<FInworldData_Text>(); FInworldData_Text != nullptr)
    {
      // InworldText is valid
    }

    // Custom Struct Data
    if (FInworldData_Struct* InworldDataStruct = InworldDataHandle->Unwrap<FInworldData_Struct>(); InworldDataStruct != nullptr)
    {
      if (FMyCustomStruct MyCustomStruct; InworldDataStruct->GetData(MyCustomStruct))
      {
        // MyCustomStruct is valid
      }
    }
    ```
  </Tab>
</Tabs>
