How to add nodes to event graph via python or other script?

From what I can tell, in Python you need to get a Blueprint object, then get the controller for the blueprint.

It’s the MVC programming pattern; model, view, controller. The model is the graph itself, the view is the viewport, and the controller is what allows the two to interact.

However, I was only able to accomplish this with ControlRig and using control rig’s python log in the messages window.

As an example, here’s the code I used in Python after getting the control rig’s Blueprint:

    def add_forward_solve(cls, bp_rig: unreal.ControlRigBlueprint):
        controller = bp_rig.get_controller_by_name('RigVMModel')

        script_path = '/Script/ControlRig.RigUnit_'

        # Add the Forward Solve Event node
        node_name = 'BeginExecution'
        controller.add_unit_node_from_struct_path(script_struct_path=script_path + node_name,
                                                  position=unreal.Vector2D(0.0, 0.0),
                                                  node_name='RigUnit_' + node_name)

        # Add the for_each node
        node = unreal.StringLibrary.conv_string_to_name(
            'DISPATCH_RigVMDispatch_ArrayIterator(in Array,out Element,out Index,out Count,out Ratio)'
        )
        controller.add_template_node(node,
                                     unreal.Vector2D(256.0, 0.0),
                                     'DISPATCH_RigVMDispatch_ArrayIterator')

        # add the GetAllControls node
        node_name = 'CollectionGetAll'
        controller.add_unit_node_from_struct_path(script_struct_path= script_path + node_name,
                                                  position=unreal.Vector2D(-160.0, 80.0),
                                                  node_name=node_name)
        controller.set_pin_default_value(pin_path='CollectionGetAll.TypeToSearch',
                                         default_value='Control',
                                         resize_arrays=False)
        controller.add_link('CollectionGetAll.Items',
                            'DISPATCH_RigVMDispatch_ArrayIterator.Array')

        # Attach Execution from Forward Solve
        controller.add_link('RigUnit_BeginExecution.ExecuteContext',
                            'DISPATCH_RigVMDispatch_ArrayIterator.ExecuteContext')

        # Get Bone Transform from Element
        node_name = 'GetTransform'
        controller.add_unit_node_from_struct_path(script_struct_path= script_path + node_name,
                                                  position=unreal.Vector2D(512.0, 208.0),
                                                  node_name=node_name)
        controller.set_pin_expansion('GetTransform.Item', False)
        controller.set_pin_default_value('GetTransform.Space', 'GlobalSpace')
        controller.set_pin_default_value('GetTransform.bInitial', 'False')
        controller.add_link('DISPATCH_RigVMDispatch_ArrayIterator.Element',
                            'GetTransform.Item')

        # Set Bone Transform
        node = unreal.StringLibrary.conv_string_to_name(
            'Set Transform::Execute(in Item,in Space,in bInitial,in Value,in Weight,in bPropagateToChildren)',
        )
        controller.add_template_node(notation=node,
                                     position=unreal.Vector2D(869.0, 0.0),
                                     node_name='Set Transform')
        controller.set_pin_expansion('Set Transform.Item', True)
        controller.set_pin_default_value('Set Transform.Item',
                                         '(Type=Bone,Name="None")')
        controller.set_pin_default_value('Set Transform.Space', 'GlobalSpace')
        controller.set_pin_default_value('Set Transform.bInitial', 'False')
        controller.set_pin_default_value('Set Transform.Weight', '1.0')
        controller.set_pin_default_value('Set Transform.bPropagateToChildren', 'True')
        controller.add_link('GetTransform.Transform', 'Set Transform.Value')

        # Add the Name replacement node
        node = unreal.StringLibrary.conv_string_to_name('Replace::Execute(in Name,in Old,in New,out Result)')
        controller.add_template_node(notation=node,
                                     position=unreal.Vector2D(576.0, 64.0),
                                     node_name='Replace')
        controller.add_link('DISPATCH_RigVMDispatch_ArrayIterator.Element.Name',
                            'Replace.Name')
        controller.set_pin_default_value('Replace.Old','CRC_', False)
        controller.add_link('Replace.Result', 'Set transform.Item.Name')
        controller.add_link('DISPATCH_RigVMDispatch_ArrayIterator.ExecuteContext',
                            'Set Transform.ExecuteContext')

Figuring out the names and types of nodes to add to the graph is probably going to be very difficult, but as I need this for work, I’ll continue to naturally chase down where these things can be found.