diff --git a/api/core/app/entities/queue_entities.py b/api/core/app/entities/queue_entities.py index 42e6a1519c..c8377a6cd1 100644 --- a/api/core/app/entities/queue_entities.py +++ b/api/core/app/entities/queue_entities.py @@ -29,6 +29,7 @@ class QueueEvent(StrEnum): WORKFLOW_SUCCEEDED = "workflow_succeeded" WORKFLOW_FAILED = "workflow_failed" WORKFLOW_PARTIAL_SUCCEEDED = "workflow_partial_succeeded" + WORKFLOW_SUSPENDED = "workflow_suspended" ITERATION_START = "iteration_start" ITERATION_NEXT = "iteration_next" ITERATION_COMPLETED = "iteration_completed" @@ -326,6 +327,13 @@ class QueueWorkflowStartedEvent(AppQueueEvent): graph_runtime_state: GraphRuntimeState +class QueueWorkflowSuspendedEvent(AppQueueEvent): + event: QueueEvent = QueueEvent.WORKFLOW_SUSPENDED + # next_node_id records the next node to execute after resuming + # workflow. + next_node_id: str + + class QueueWorkflowSucceededEvent(AppQueueEvent): """ QueueWorkflowSucceededEvent entity diff --git a/api/core/workflow/entities/workflow_execution.py b/api/core/workflow/entities/workflow_execution.py index 781be4b3c6..a43d75bbed 100644 --- a/api/core/workflow/entities/workflow_execution.py +++ b/api/core/workflow/entities/workflow_execution.py @@ -23,12 +23,109 @@ class WorkflowType(StrEnum): class WorkflowExecutionStatus(StrEnum): + # State diagram for the workflw status: + # (@) means start, (*) means end + # + # ┌------------------>------------------------->------------------->--------------┐ + # | | + # | ┌-----------------------<--------------------┐ | + # ^ | | | + # | | ^ | + # | V | | + # ┌-----------┐ ┌-----------------------┐ ┌-----------┐ V + # | Scheduled |------->| Running |---------------------->| Suspended | | + # └-----------┘ └-----------------------┘ └-----------┘ | + # | | | | | | | + # | | | | | | | + # ^ | | | V V | + # | | | | | ┌---------┐ | + # (@) | | | └------------------------>| Stopped |<----┘ + # | | | └---------┘ + # | | | | + # | | V V + # | | ┌-----------┐ | + # | | | Succeeded |------------->--------------┤ + # | | └-----------┘ | + # | V V + # | +--------┐ | + # | | Failed |---------------------->----------------┤ + # | └--------┘ | + # V V + # ┌---------------------┐ | + # | Partially Succeeded |---------------------->-----------------┘--------> (*) + # └---------------------┘ + # + # Mermaid diagram: + # + # --- + # title: State diagram for Workflow run state + # --- + # stateDiagram-v2 + # scheduled: Scheduled + # running: Running + # succeeded: Succeeded + # failed: Failed + # partial_succeeded: Partial Succeeded + # suspended: Suspended + # stopped: Stopped + # + # [*] --> scheduled: + # scheduled --> running: Start Execution + # running --> suspended: Human input required + # suspended --> running: human input added + # suspended --> stopped: User stops execution + # running --> succeeded: Execution finishes without any error + # running --> failed: Execution finishes with errors + # running --> stopped: User stops execution + # running --> partial_succeeded: some execution occurred and handled during execution + # + # scheduled --> stopped: User stops execution + # + # succeeded --> [*] + # failed --> [*] + # partial_succeeded --> [*] + # stopped --> [*] + + # `SCHEDULED` means that the workflow is scheduled to run, but has not + # started running yet. (maybe due to possible worker saturation.) + SCHEDULED = "scheduled" + + # `RUNNING` means the workflow is exeuting. RUNNING = "running" + + # `SUCCEEDED` means the execution of workflow succeed without any error. SUCCEEDED = "succeeded" + + # `FAILED` means the execution of workflow failed without some errors. FAILED = "failed" + + # `STOPPED` means the execution of workflow was stopped, either manually + # by the user, or automatically by the Dify application (E.G. the moderation + # mechanism.) STOPPED = "stopped" + + # `PARTIAL_SUCCEEDED` indicates that some errors occurred during the workflow + # execution, but they were successfully handled (e.g., by using an error + # strategy such as "fail branch" or "default value"). PARTIAL_SUCCEEDED = "partial-succeeded" + # `SUSPENDED` indicates that the workflow execution is temporarily paused + # (e.g., awaiting human input) and is expected to resume later. + SUSPENDED = "suspended" + + def is_ended(self) -> bool: + return self in _END_STATE + + +_END_STATE = frozenset( + [ + WorkflowExecutionStatus.SUCCEEDED, + WorkflowExecutionStatus.FAILED, + WorkflowExecutionStatus.PARTIAL_SUCCEEDED, + WorkflowExecutionStatus.STOPPED, + ] +) + class WorkflowExecution(BaseModel): """ diff --git a/api/core/workflow/graph_engine/entities/event.py b/api/core/workflow/graph_engine/entities/event.py index e57e9e4d64..d44b4e0418 100644 --- a/api/core/workflow/graph_engine/entities/event.py +++ b/api/core/workflow/graph_engine/entities/event.py @@ -43,6 +43,10 @@ class GraphRunPartialSucceededEvent(BaseGraphEvent): outputs: Optional[dict[str, Any]] = None +class GraphRunSuspendedEvent(BaseGraphEvent): + next_node_id: str = Field(..., description="the next node id to execute while resumed.") + + ########################################### # Node Events ###########################################