===== Usage ===== Workforce provides several ways to create and run workflows. You can use the GUI for visual editing, or the CLI for programmatic control. Command Line Interface ---------------------- Launching the GUI ~~~~~~~~~~~~~~~~~ To launch the Workforce GUI: .. code-block:: bash wf or: .. code-block:: bash python -m workforce To open a specific workflow file: .. code-block:: bash wf path/to/workflow.graphml If a ``Workfile`` exists in the current directory, it will be opened automatically: .. code-block:: bash wf Running Workflows ~~~~~~~~~~~~~~~~~ Execute a complete workflow: .. code-block:: bash wf run Workfile Execute specific nodes only: .. code-block:: bash wf run Workfile --nodes node1,node2,node3 Use command wrappers (prefix/suffix): .. code-block:: bash wf run Workfile --wrapper "docker run -it ubuntu" Server Management ~~~~~~~~~~~~~~~~~ Start a server for a workflow: .. code-block:: bash wf server start Workfile Stop a server: .. code-block:: bash wf server stop Workfile List all running servers: .. code-block:: bash wf server list Editing Workflows via CLI ~~~~~~~~~~~~~~~~~~~~~~~~~~ Add a node to a workflow: .. code-block:: bash wf edit add-node Workfile "node_name" "command to run" Remove a node: .. code-block:: bash wf edit remove-node Workfile "node_name" Add an edge (dependency): .. code-block:: bash wf edit add-edge Workfile "source_node" "target_node" Remove an edge: .. code-block:: bash wf edit remove-edge Workfile "source_node" "target_node" Edit node command: .. code-block:: bash wf edit edit-node Workfile "node_name" "new command" Edit node position (for GUI layout): .. code-block:: bash wf edit edit-node-position Workfile "node_name" x y GUI Usage --------- The Workforce GUI provides an interactive visual editor for workflows. Keyboard Shortcuts ~~~~~~~~~~~~~~~~~~ * **Q** - Save and exit * **R** - Run the workflow (or run selected nodes) * **D** / **Delete** / **Backspace** - Delete selected node(s) * **E** - Connect selected nodes in sequence (creates edges) * **Shift+E** - Clear edges from selected nodes * **C** - Clear status of selected nodes * **Shift+C** - Clear all statuses in workflow * **S** - View logs for selected node * **P** - Edit command wrapper * **O** - Load/open Workfile * **Ctrl+S** - Save workflow * **Ctrl+Up** / **Ctrl+Down** - Zoom in/out * **Double-Click Canvas** - Add new node at cursor position * **Double-Click Node** - Edit node command * **Right-Click + Drag** - Create edge from source to target node * **Middle-Click Node** - Select node * **Left-Click + Drag Node** - Move node(s) * **Shift + Left-Click + Drag Canvas** - Rectangle selection Edge Types ~~~~~~~~~~ Workforce supports two types of edges that define how nodes depend on each other. See the :ref:`glossary` for detailed definitions. **Blocking Edges** (:ref:`blocking-edge`) Blocking edges are the default edge type and enforce strict dependencies. When a node has blocking edges as inputs, it only transitions to ``run`` state after **all** incoming blocking edges are ready. This enforces sequential execution. To create a blocking edge (default behavior): 1. Right-click on a source node 2. Drag to the target node 3. Release to create the edge 4. The edge will appear as a solid line Via CLI: .. code-block:: bash wf edit add-edge Workfile "source_node" "target_node" This creates a blocking edge by default. Via REST API (blocking edge): .. code-block:: bash curl -X POST http://localhost:5049/workspace/workspace_id/add-edge \ -H "Content-Type: application/json" \ -d '{"source_id": "node-uuid-1", "target_id": "node-uuid-2", "edge_type": "blocking"}' Via Python client: .. code-block:: python from workforce.gui.client import ServerClient client = ServerClient(server_url) client.add_edge(source_id="node-uuid-1", target_id="node-uuid-2", edge_type="blocking") **Non-Blocking Edges** (:ref:`non-blocking-edge`) Non-blocking edges are soft triggers that allow immediate execution without waiting for other dependencies. When a non-blocking edge becomes ready, the target node immediately transitions to ``run`` state, allowing for flexible triggering and re-execution patterns. To create a non-blocking edge: 1. Hold **Ctrl+Shift** 2. Right-click and drag from source node to target node 3. Release to create the non-blocking edge 4. The edge will appear as a dashed line Via CLI: .. code-block:: bash wf edit add-edge Workfile "source_node" "target_node" --edge_type non-blocking Via REST API (non-blocking edge): .. code-block:: bash curl -X POST http://localhost:5049/workspace/workspace_id/add-edge \ -H "Content-Type: application/json" \ -d '{"source_id": "node-uuid-1", "target_id": "node-uuid-2", "edge_type": "non-blocking"}' Via Python client: .. code-block:: python from workforce.gui.client import ServerClient client = ServerClient(server_url) client.add_edge(source_id="node-uuid-1", target_id="node-uuid-2", edge_type="non-blocking") **Updating Edge Types** Change an existing edge type: Via CLI: .. code-block:: bash wf edit edit-edge-type Workfile "source_node" "target_node" "non-blocking" Via REST API: .. code-block:: bash curl -X POST http://localhost:5049/workspace/workspace_id/edit-edge-type \ -H "Content-Type: application/json" \ -d '{"source_id": "node-uuid-1", "target_id": "node-uuid-2", "edge_type": "non-blocking"}' **Use Cases** **Blocking Edges** are appropriate for: * Sequential pipelines where each step must complete before the next starts * Workflows that conform to directed acyclic graph (DAG) structure * Ensuring all prerequisites are satisfied before proceeding * Traditional data processing pipelines (extract → transform → load) **Non-Blocking Edges** are appropriate for: * Fan-out patterns where a single node triggers multiple independent branches * Workflows requiring node re-execution (e.g., error recovery, data reprocessing) * Event-driven execution where triggers are more important than strict ordering * Flexible workflows that don't conform to strict DAG structure * Monitoring or signal nodes that notify multiple consumers **Mixed Edge Workflows** Workflows can combine both edge types for sophisticated execution patterns. For example: * A node with both blocking edges (ensuring prerequisites) and non-blocking edges (allowing immediate re-execution on external triggers) * Multiple independent branches triggered by a single source (blocking to first node, then non-blocking to branch starts) * See :ref:`dependency-resolution` in the architecture documentation for detailed execution semantics Creating Workflows ~~~~~~~~~~~~~~~~~~ 1. Launch the GUI with ``wf`` 2. Double-click on the canvas to add a new node 3. Enter the bash command in the popup dialog 4. To create dependencies: * Right-click and drag from source node to target node, OR * Select multiple nodes and press 'E' to connect them in sequence 5. Save the workflow (press Ctrl+S or use File menu) Running Workflows ~~~~~~~~~~~~~~~~~ From the GUI: 1. Click the 'Run' button or press 'R' 2. If nodes are selected, only those nodes (and their dependencies) will run 3. Otherwise, the entire workflow executes 4. Node colors indicate status: * Light gray - Not started (empty status) * Light cyan - Ready to run (status: ``run``) * Light blue - Currently running (status: ``running``) * Light green - Completed successfully (status: ``ran``) * Light coral/red - Failed (status: ``fail``) Viewing Logs ~~~~~~~~~~~~ To view output from a node: 1. Select the node (left-click) 2. Press 'S' to open the log viewer 3. In the log popup, press 'S' or Escape to close it 4. View combined stdout/stderr from the command execution Workflow File Format -------------------- Workforce uses GraphML format to store workflows. Each node represents a bash command, and edges represent dependencies. Example GraphML Structure ~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: xml wget https://example.com/data.csv 100 100 python process.py data.csv 300 100 Node Attributes ~~~~~~~~~~~~~~~ * **id** - Unique identifier (UUID) * **label** - The bash command to execute * **status** - Current execution status ("", run, running, ran, fail) * **log** - Combined stdout/stderr from command execution * **x, y** - Node position in GUI canvas (stored as strings) Edge Attributes ~~~~~~~~~~~~~~~ * **id** - Unique identifier (UUID) * **status** - Edge status used for dependency tracking ("", to_run) Command Wrappers ---------------- Workforce allows you to wrap commands with a template that includes a ``{}`` placeholder. This is useful for running commands in different environments. The wrapper is a command template where ``{}`` is replaced with the actual node command. Wrapper Examples ~~~~~~~~~~~~~~~~ **tmux Integration** Send commands to tmux sessions: .. code-block:: bash wf run Workfile --wrapper 'tmux send-keys -t mysession "{}" C-m' **Remote Execution via SSH** Execute commands on a remote server: .. code-block:: bash wf run Workfile --wrapper 'ssh user@remote-server "{}"' **Docker Containers** Run commands inside Docker containers: .. code-block:: bash wf run Workfile --wrapper 'docker run -it ubuntu bash -c "{}"' **Conda Environments** Activate a conda environment before running: .. code-block:: bash wf run Workfile --wrapper 'conda run -n myenv bash -c "{}"' **Slurm Job Submission** Submit each command as a Slurm job: .. code-block:: bash wf run Workfile --wrapper 'sbatch --wrap="{}"' **Export to Bash Script** Generate a bash script without executing: .. code-block:: bash wf run Workfile --wrapper 'echo "{}" >> commands.sh' **Adding Sleep/Delay** Add delay before each command: .. code-block:: bash wf run Workfile --wrapper 'bash -c "sleep 1; {}"' Python API ---------- You can also use Workforce programmatically: .. code-block:: python import workforce from workforce.edit.graph import load_graph, add_node_to_graph, save_graph # Load an existing workflow G = load_graph('workflow.graphml') # Add a new node (returns dict with node_id) result = add_node_to_graph('workflow.graphml', 'echo "Hello World"', x=100, y=200) print(f"Created node: {result['node_id']}") # Note: Most graph operations save automatically # Individual functions like add_node_to_graph() handle save internally For running workflows programmatically, connect to the server: .. code-block:: python from workforce import utils # Compute workspace ID from file path workspace_id = utils.compute_workspace_id('workflow.graphml') # Get workspace URL (auto-discovers or starts server) workspace_url = utils.get_workspace_url(workspace_id) # Runner client connects to workspace URL and waits for node_ready events # This is typically done by the run command, not manually Remote GUI over LAN ------------------- You can run the GUI from a different machine and connect to a remote server. - Start the server with LAN binding on the host machine: .. code-block:: bash wf server stop wf server start --host 0.0.0.0 - List access URLs and share the workspace URL: .. code-block:: bash wf server ls This shows both Local and LAN URLs. Use the LAN URL format: .. code-block:: text http://:/workspace/ - From the remote machine, launch the GUI directly to that URL: .. code-block:: bash wf gui http://:/workspace/ Notes: - Do not use any 127.* addresses across machines; those are loopback only. - macOS: Allow the Python process through the firewall when prompted. - Termux/Android: Ensure the app has network permissions and the device is on the same subnet. WSL (Windows Subsystem for Linux) --------------------------------- WSL2 uses NAT and assigns a private IP to the Linux VM. Services bound inside WSL are reachable from the Windows host, but not automatically from other LAN machines. Options to access the Workforce server from other machines: 1) Run the server on Windows (outside WSL) .. code-block:: bash # In Windows Python environment wf server start --host 0.0.0.0 Then use the Windows host LAN IP in the URL. 2) Port forward from Windows host to WSL - Find the current WSL IP (inside WSL): .. code-block:: bash ip addr | grep 'inet ' - Add a Windows port proxy to forward port 5049 to WSL (run in an elevated PowerShell or cmd): .. code-block:: text netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=5049 connectaddress= connectport=5049 - Open firewall for inbound TCP 5049: .. code-block:: text netsh advfirewall firewall add rule name="Workforce 5049" dir=in action=allow protocol=TCP localport=5049 - Verify: .. code-block:: text netsh interface portproxy show all - Use the Windows host LAN IP in the GUI URL: .. code-block:: bash wf gui http://:5049/workspace/ Note: WSL IP can change after restart. Recreate the portproxy if needed. Security -------- For LAN use, Workforce enables CORS for Socket.IO in development. If exposing beyond LAN, use a reverse proxy (e.g., Nginx) with TLS and restrict allowed origins.