# Copyright (c) 2025 Patrick Motsch # Input/Human node executor - creates tasks and pauses execution. import logging from typing import Dict, Any logger = logging.getLogger(__name__) class PauseForHumanTaskError(Exception): """Raised when execution must pause for a human task. Contains runId, taskId.""" def __init__(self, runId: str, taskId: str, nodeId: str): self.runId = runId self.taskId = taskId self.nodeId = nodeId super().__init__(f"Pause for human task {taskId} (run {runId}, node {nodeId})") class InputExecutor: """ Execute input/human nodes. Creates a HumanTask, pauses the run, and raises PauseForHumanTaskError so the engine returns { paused: true, taskId, runId }. """ def __init__(self, automation2_interface: Any): self.automation2 = automation2_interface async def execute( self, node: Dict[str, Any], context: Dict[str, Any], ) -> Any: nodeType = node.get("type", "") nodeId = node.get("id", "") runId = context.get("_runId") workflowId = context.get("workflowId") instanceId = context.get("instanceId") userId = context.get("userId") if not runId or not workflowId: logger.error("InputExecutor: runId/workflowId missing in context - cannot create task") return {"error": "Missing run context", "success": False} config = dict(node.get("parameters") or {}) logger.info("InputExecutor node %s type=%s creating task", nodeId, nodeType) task = self.automation2.createTask( runId=runId, workflowId=workflowId, nodeId=nodeId, nodeType=nodeType, config=config, assigneeId=userId, ) taskId = task.get("id") self.automation2.updateRun( runId, status="paused", nodeOutputs=context.get("nodeOutputs"), currentNodeId=nodeId, context={ "connectionMap": context.get("connectionMap"), "inputSources": context.get("inputSources"), "orderedNodeIds": [n.get("id") for n in context.get("_orderedNodes", []) if n.get("id")], }, ) logger.info("InputExecutor node %s: created task %s, run %s paused", nodeId, taskId, runId) raise PauseForHumanTaskError(runId=runId, taskId=taskId, nodeId=nodeId)