engine

package
v1.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 7, 2026 License: MIT Imports: 25 Imported by: 0

Documentation

Index

Constants

View Source
const (
	EditReplaceStartImage = "replace_start_image"
	EditReplaceEndImage   = "replace_end_image"
	EditUserPrompt        = "edit_user_prompt"
)
View Source
const (
	DirtyReasonInputChanged   = "input_changed"
	DirtyReasonUpstreamDirty  = "upstream_dirty"
	DirtyReasonMissingParent  = "missing_parent_snapshot"
	DirtyReasonInputResolve   = "input_resolve_failed"
	DirtyReasonParentNotReady = "parent_not_success"

	DirtyReasonPatchedState   = "patched_state"
	DirtyReasonResumeBoundary = "resume_boundary"
)
View Source
const AwaitUsageFactsMetaKey = "__usage_facts"
View Source
const CancelReasonSupersededByRevision = "superseded_by_revision"
View Source
const (
	TaskNoRetryFound = "no retry root found task"
)

Variables

View Source
var (
	ErrRunCancellationTaskNotFound = errors.New("run cancellation: task not found")
	ErrRunCancellationNotAllowed   = errors.New("run cancellation: task status cannot be canceled")
)
View Source
var ErrNoJob = errors.New("async job queue: no job available")

ErrNoJob 表示当前没有可消费的异步任务(空轮询)。 队列实现(如 Redis Stream 的 block 超时)应返回该哨兵错误, 消费方据此静默重试,无需感知底层驱动的具体错误类型。

View Source
var ErrTaskCanceled = errors.New("task already canceled")

Functions

func DeleteByPath

func DeleteByPath(root map[string]any, path string) error

func GetByPath

func GetByPath(root map[string]any, path string) (any, bool)

func MergeByPath

func MergeByPath(root map[string]any, path string, value any) error

func SetByPath

func SetByPath(root map[string]any, path string, value any) error

Types

type AsyncJob

type AsyncJob struct {
	TaskID      int64          `json:"task_id"`
	Node        string         `json:"node"`
	StepAdapter string         `json:"step_adapter"`
	Input       map[string]any `json:"input"`
	Hash        string         `json:"hash"`
}

type AsyncJobQueue

type AsyncJobQueue interface {

	// 发布任务
	Publish(ctx context.Context, job AsyncJob) error

	// Worker消费
	Consume(ctx context.Context, group string, consumer string) (*AsyncJob, string, error)

	// ack
	Ack(ctx context.Context, id string) error
}

type AwaitTraceInfo

type AwaitTraceInfo struct {
	AwaitType        domain.AwaitType          `json:"await_type"`
	Source           domain.AwaitSource        `json:"source"`
	Status           domain.AwaitBindingStatus `json:"status"`
	Provider         *string                   `json:"provider,omitempty"`
	ProviderTaskID   *string                   `json:"provider_task_id,omitempty"`
	ResultPayload    map[string]any            `json:"result_payload,omitempty"`
	ErrorMessage     string                    `json:"error_message,omitempty"`
	PollAttempts     int                       `json:"poll_attempts,omitempty"`
	WaitingStartedAt *time.Time                `json:"waiting_started_at,omitempty"`
	CompletedAt      *time.Time                `json:"completed_at,omitempty"`
}

AwaitTraceInfo await 节点的挂起/回调详情

type CheckpointOutputRebuilder

type CheckpointOutputRebuilder func(
	nodeDef nodes.Node,
	runtime *domain.NodeRuntime,
) (map[string]any, error)

type ClosureValidationIssue

type ClosureValidationIssue struct {
	Level     ClosureValidationLevel `json:"level"`
	NodeName  string                 `json:"node_name"`
	EdgeKey   string                 `json:"edge_key,omitempty"`
	FieldName string                 `json:"field_name,omitempty"`
	Message   string                 `json:"message"`
}

ClosureValidationIssue describes a single state closure problem.

type ClosureValidationLevel

type ClosureValidationLevel string

ClosureValidationLevel indicates severity.

const (
	ClosureLevelBlock ClosureValidationLevel = "block"
)

type ClosureValidationMode

type ClosureValidationMode string

ClosureValidationMode controls strictness per entry point.

const (
	ClosureModeFork   ClosureValidationMode = "fork"   // strictest — no unstable or awaiting nodes
	ClosureModeResume ClosureValidationMode = "resume" // allow awaiting / suspended
)

type ClosureValidationResult

type ClosureValidationResult struct {
	Valid  bool                     `json:"valid"`
	Issues []ClosureValidationIssue `json:"issues,omitempty"`
}

ClosureValidationResult aggregates all validation issues.

func ValidateParentStateClosure

func ValidateParentStateClosure(
	parentNodes map[string]*domain.NodeRuntime,
	dag *graph.Graph,
	mode ClosureValidationMode,
) *ClosureValidationResult

ValidateParentStateClosure checks the parent task's node runtime states for DAG state closure consistency before fork/redo/resume operations.

type DirtyPlan

type DirtyPlan struct {
	DirtyNodes   map[string]string
	ReuseNodes   map[string]bool
	MapItemReuse map[string]map[int]bool
	PatchedNodes map[string]bool
}

type EdgeRuntimeState

type EdgeRuntimeState string

EdgeRuntimeState 运行时边三态推导。 ActivatedEdges 持久化仍为 bool(不存三态),但所有调度判断统一通过 resolveEdgeState 从 edge.Type + parent.State + ActivatedEdges[key] 推导, 解决「false 同时表示条件未命中和必选父阻塞」的语义漏洞。

const (
	// EdgeStateUnknown — 边状态尚未确定(父未 terminal 或 edge 未计算)。
	EdgeStateUnknown EdgeRuntimeState = "unknown"
	// EdgeStateActive — 边被激活且父节点成功,参与下游 join。
	EdgeStateActive EdgeRuntimeState = "active"
	// EdgeStateInactive — 条件边未命中(仅限父节点成功 + 条件不满足),
	// 该边从下游 join 剔除。
	EdgeStateInactive EdgeRuntimeState = "inactive"
	// EdgeStateBlocked — 父节点 terminal 但非 Success(failed/skipped/canceled)。
	// 无论 EdgeNormal 还是 EdgeCondition,父死即路径阻塞,不允许下游执行。
	EdgeStateBlocked EdgeRuntimeState = "blocked"
)

type Engine

type Engine struct {
	WorkflowVersionRepo repository.WorkflowVersionRepository
	WorkflowRepo        repository.WorkflowRepository
	// contains filtered or unexported fields
}

Engine 工作流执行器

func New

func New(opts ...EngineOption) *Engine

New creates an Engine from functional options.

The options-based constructor is the preferred way to create an Engine. The legacy NewEngine(a,b,c,...) positional constructor remains for backward compatibility.

func NewEngine

func NewEngine(
	taskRepo repository.TaskRepository,
	nodeRepo repository.NodeRuntimeRepository,
	awaitBindingRepo repository.AwaitBindingRepository,
	workflowVersionRepo repository.WorkflowVersionRepository,
	workflowRepo repository.WorkflowRepository,
	builder *workflow.Builder,
	eventBus *eventbus.EventBus,
	jobQueue AsyncJobQueue,
	dLocker lock.DistributedLock,
	eventRepo repository.EventRepository,
) *Engine

func (*Engine) BuildDirtyPlan

func (e *Engine) BuildDirtyPlan(
	runCtx *nodes.Context,
	wf workflow.Workflow,
	newInput map[string]any,
) (*DirtyPlan, error)

BuildDirtyPlan

规则: 1. parent snapshot success 全量 seed 到 planCtx 2. patch 应用到 planCtx 3. topo 顺序重新构造 input 4. 普通 hash 对比只决定“可否 reuse” 5. patched node 和 resume_from 再走优先级覆盖

func (*Engine) BuildRunPlan

func (e *Engine) BuildRunPlan(
	runCtx *nodes.Context,
	wf workflow.Workflow,
	newInput map[string]any,
) (*RunPlan, error)

func (*Engine) Close

func (e *Engine) Close()

Close 停止引擎的事件监听 goroutine 并等待其退出。 幂等;Close 之后引擎不应再执行任务。

func (*Engine) CompleteAwaitNode

func (e *Engine) CompleteAwaitNode(
	bindingID int64,
	eventPayload map[string]any,
	eventErr string,
	source string,
) RunResult

func (*Engine) CompleteNodeAndResume

func (e *Engine) CompleteNodeAndResume(
	taskID int64,
	nodeName string,
	meta map[string]any,
	errMsg string,
) RunResult

CompleteNodeAndResume 外部事件唤醒挂起任务的统一入口: 用 meta 闭合 nodeName 节点(写输出、状态转 success-pending-edges),随后继续执行 DAG。

若该节点存在等待中的 await binding(await 节点挂起),走 CompleteAwaitNode 保证 binding 状态一并闭合;否则走 async 节点的抢占式闭合 + ResumeTask。 节点已被其他线程处理时返回 RunNoop,可安全重复调用。

func (*Engine) CreateForkRun

func (e *Engine) CreateForkRun(
	ctx context.Context,
	sourceTaskID int64,
	userID int64,
	overrideInput map[string]any,
	resumeSpec *domain.ResumeSpec,
	editAction string,
	editLabel string,
) (*domain.Task, error)

func (*Engine) GenTaskID

func (e *Engine) GenTaskID() int64

func (*Engine) LoadForkParentSnapshot

func (e *Engine) LoadForkParentSnapshot(runCtx *nodes.Context) error

func (*Engine) MaterializeRunPlan

func (e *Engine) MaterializeRunPlan(
	runCtx *nodes.Context,
	wf workflow.Workflow,
	plan *RunPlan,
) error

func (*Engine) NodeRepo

func (e *Engine) NodeRepo() repository.NodeRuntimeRepository

func (*Engine) PreviewRunPlan

func (e *Engine) PreviewRunPlan(
	ctx context.Context,
	sourceTask *domain.Task,
	resumeSpec *domain.ResumeSpec,
	overrideInput map[string]any,
) (*RunPlan, workflow.Workflow, error)

PreviewRunPlan

用“源任务 + 临时 resumeSpec + overrideInput”构造一个纯内存 preview runCtx, 然后直接复用真实 BuildRunPlan 逻辑,返回可视化调试用的 RunPlan。 注意: 1. 不创建 task 2. 不写数据库 3. 不 materialize

func (*Engine) ReconcileSubWorkflowBinding

func (e *Engine) ReconcileSubWorkflowBinding(bindingID int64) RunResult

ReconcileSubWorkflowBinding 是 subworkflow await binding 的 poll 对账兜底(P2)。

AwaitPollWorker 周期性对 due 的 subworkflow binding 调用本方法:直接查子任务的真实终态, 不依赖会丢的进程内事件,从而修复"子任务已完成但父任务没被唤醒"的静默挂起 (P1 把父节点落到 NodeAwaiting 退出了 heartbeat scanner,这条 poll 兜底就是它的安全网)。

与事件快路径(event_listen.go)汇聚到同一个 CompleteAwaitNode,靠 ClaimCompleting 原子去重, 两路只生效一次。子任务仍在执行时只重排下次对账;子任务终态失败/取消时把 binding 完成为失败, 父任务重试时由 RunSubWorkflow 的 TaskFailed/TaskCanceled 分支(Fix 4)负责复活,沿用既有语义。

func (*Engine) Replay

func (e *Engine) Replay(ctx context.Context, taskID int64) (*ReplayTrace, error)

Replay 基于已持久化的执行状态,对指定任务做纯读回放,不调用任何工具或写入任何数据。 返回结构化 trace,包含每个节点的解析后入参、输出、分支决策。

func (*Engine) ReplayToWS

func (e *Engine) ReplayToWS(ctx context.Context, taskID int64, speedMs int) error

ReplayToWS 将已持久化的原始事件按 sequence 顺序重发到 task:{taskID} channel。 客户端订阅方式与真实任务完全一致,收到的事件类型也完全相同。 speedMs 控制事件间推送延迟(毫秒),0 表示无延迟。

func (*Engine) ResumeTask

func (e *Engine) ResumeTask(
	taskID int64,
	nodeName string,
	meta map[string]any,
) RunResult

ResumeTask 恢复 Workflow 任务执行

func (*Engine) Run

func (e *Engine) Run(
	ctx context.Context,
	task *domain.Task,
	def *definition.WorkflowDefinition,
) error

Run 执行 DAG,保留旧接口语义: 只有失败时返回 error,success/suspended/noop 都返回 nil。

func (*Engine) RunSubWorkflow

func (e *Engine) RunSubWorkflow(
	execCtx *nodes.NodeExecContext,
	workflowName string,
	input map[string]any,
) (map[string]any, error)

func (*Engine) RunWithResult

func (e *Engine) RunWithResult(
	ctx context.Context,
	task *domain.Task,
	def *definition.WorkflowDefinition,
) RunResult

RunWithResult 执行 DAG,并返回明确的运行结果状态。

func (*Engine) SetCostRecorder

func (e *Engine) SetCostRecorder(recorder cost.Recorder)

func (*Engine) SetSubWorkflowBinding

func (e *Engine) SetSubWorkflowBinding(enabled bool)

SetSubWorkflowBinding 开启/关闭 subworkflow-as-await-binding 写入路径(P1,默认关闭)。

func (*Engine) TaskRepo

func (e *Engine) TaskRepo() repository.TaskRepository

func (*Engine) ValidateResumeSpecForExternal

func (e *Engine) ValidateResumeSpecForExternal(
	wf workflow.Workflow,
	resumeSpec *domain.ResumeSpec,
) error

type EngineOption

type EngineOption func(*engineConfig)

EngineOption is a functional option for configuring an Engine.

func WithAwaitBindingRepo

func WithAwaitBindingRepo(r repository.AwaitBindingRepository) EngineOption

WithAwaitBindingRepo enables await/signal nodes.

func WithBuilder

func WithBuilder(b *workflow.Builder) EngineOption

WithBuilder is required — compiles WorkflowDefinition into executable Workflow.

func WithCostRecorder

func WithCostRecorder(r cost.Recorder) EngineOption

WithCostRecorder sets a custom cost recorder (default: noop).

func WithDistributedLock

func WithDistributedLock(l lock.DistributedLock) EngineOption

WithDistributedLock enables sub-workflow coordination across workers.

func WithEventBus

func WithEventBus(b *eventbus.EventBus) EngineOption

WithEventBus is required — publishes task/node events.

func WithEventRepo

func WithEventRepo(r repository.EventRepository) EngineOption

WithEventRepo is required — persists task events.

func WithJobQueue

func WithJobQueue(q AsyncJobQueue) EngineOption

WithJobQueue is required — enables async node execution and sub-workflow scheduling.

func WithNodeRepo

WithNodeRepo is required — stores and retrieves per-node runtime state.

func WithSubWorkflowBinding added in v1.0.3

func WithSubWorkflowBinding(enabled bool) EngineOption

func WithTaskRepo

func WithTaskRepo(r repository.TaskRepository) EngineOption

WithTaskRepo is required — stores and retrieves task state.

func WithWorkflowRepo

func WithWorkflowRepo(r repository.WorkflowRepository) EngineOption

WithWorkflowRepo is required — loads workflow metadata.

func WithWorkflowVersionRepo

func WithWorkflowVersionRepo(r repository.WorkflowVersionRepository) EngineOption

WithWorkflowVersionRepo is required — loads workflow definitions from storage.

type ExecutionReason

type ExecutionReason string
const (
	ExecutionReasonNone             ExecutionReason = ""
	ExecutionReasonReuseNode        ExecutionReason = "reuse_node"
	ExecutionReasonPatchedNode      ExecutionReason = "patched_node"
	ExecutionReasonResumeBoundary   ExecutionReason = "resume_boundary"
	ExecutionReasonUpstreamDirty    ExecutionReason = "upstream_dirty"
	ExecutionReasonInputChanged     ExecutionReason = "input_changed"
	ExecutionReasonMissingParent    ExecutionReason = "missing_parent_snapshot"
	ExecutionReasonParentNotReady   ExecutionReason = "parent_not_success"
	ExecutionReasonInputResolveFail ExecutionReason = "input_resolve_failed"
)

type MapItemFrame

type MapItemFrame struct {
	Index  int            `json:"index"`
	Output map[string]any `json:"output"`
	Reused bool           `json:"reused"`
}

MapItemFrame map 节点逐 item 结果

type NodePlan

type NodePlan struct {
	Name         string
	Label        string
	NodeType     definition.NodeType
	Action       PlanAction
	Reason       ExecutionReason
	ReuseKind    domain.ReuseKind
	MapItemReuse map[int]bool
	Patches      []domain.RuntimePatch

	// 调试 / 观测信息
	ParentTaskID *int64
	ParentNode   *string
}

type NodeReplayResult

type NodeReplayResult struct {
	TaskID         int64          `json:"task_id"`
	NodeName       string         `json:"node_name"`
	NodeState      string         `json:"node_state"`
	NodeType       string         `json:"node_type"`
	Tool           string         `json:"tool,omitempty"`
	ResolvedInput  map[string]any `json:"resolved_input,omitempty"`
	OriginalOutput map[string]any `json:"original_output,omitempty"`
	OriginalError  string         `json:"original_error,omitempty"`
	ReplayOutput   map[string]any `json:"replay_output,omitempty"`
	ReplayError    string         `json:"replay_error,omitempty"`
	Executed       bool           `json:"executed"`
}

type NodeReplayService

type NodeReplayService interface {
	ReplayTaskNode(ctx context.Context, taskID int64, nodeName string, execute bool) (*NodeReplayResult, error)
}

func NewNodeReplayService

func NewNodeReplayService(
	taskRepo repository.TaskRepository,
	nodeRuntimeRepo repository.NodeRuntimeRepository,
	workflowVersionRepo repository.WorkflowVersionRepository,
	replayEngine *Engine,
	toolRegistry *tool.Registry,
) NodeReplayService

type NodeTraceFrame

type NodeTraceFrame struct {
	Name             string           `json:"name"`
	Index            int              `json:"index"`
	BizIndex         int              `json:"biz_index"`
	State            domain.NodeState `json:"state"`
	StartedAt        *time.Time       `json:"started_at,omitempty"`
	FinishedAt       *time.Time       `json:"finished_at,omitempty"`
	DurationMs       *int64           `json:"duration_ms,omitempty"`
	ResolvedInput    map[string]any   `json:"resolved_input"`
	Output           map[string]any   `json:"output"`
	OutputPersisted  bool             `json:"output_persisted"`
	ActivatedEdges   map[string]bool  `json:"activated_edges"`
	ReuseKind        domain.ReuseKind `json:"reuse_kind,omitempty"`
	ReusedFromTaskID *int64           `json:"reused_from_task_id,omitempty"`
	Error            string           `json:"error,omitempty"`
	// Await 节点附加信息
	AwaitInfo *AwaitTraceInfo `json:"await_info,omitempty"`
	// Map/Loop 节点逐 item 结果
	MapItems []MapItemFrame `json:"map_items,omitempty"`
}

NodeTraceFrame 单个节点的回放帧

type ParentFanoutNodeKind

type ParentFanoutNodeKind string
const (
	ParentFanoutNodeNone ParentFanoutNodeKind = ""
	ParentFanoutNodeMap  ParentFanoutNodeKind = "map"
	ParentFanoutNodeLoop ParentFanoutNodeKind = "loop"
)

type PlanAction

type PlanAction string
const (
	PlanActionReuse   PlanAction = "reuse"   // 直接复用父快照并注入 success
	PlanActionPatch   PlanAction = "patch"   // 复用父快照后打 patch,patched success
	PlanActionExecute PlanAction = "execute" // 进入 pending,等待 runDAG 真执行
)

type ReplayTrace

type ReplayTrace struct {
	TaskID            int64             `json:"task_id"`
	WorkflowVersionID int64             `json:"workflow_version_id"`
	Status            domain.TaskStatus `json:"status"`
	StartedAt         time.Time         `json:"started_at"`
	FinishedAt        *time.Time        `json:"finished_at,omitempty"`
	Input             map[string]any    `json:"input"`
	Nodes             []NodeTraceFrame  `json:"nodes"`
}

ReplayTrace 一次任务执行的完整回放结果

type RetryTrigger

type RetryTrigger string
const (
	RetryTriggerManual   RetryTrigger = "manual"
	RetryTriggerRecovery RetryTrigger = "recovery_scanner"
)

type RunCancellationResult

type RunCancellationResult struct {
	TaskID                  int64
	Reason                  string
	AlreadyCanceled         bool
	CanceledTaskIDs         []int64
	CanceledNodeIDs         []int64
	CanceledAwaitBindingIDs []int64
}

type RunCancellationService

type RunCancellationService interface {
	CancelForSupersededRevision(ctx context.Context, taskID int64) (*RunCancellationResult, error)
}

type RunPlan

type RunPlan struct {
	TaskID       int64
	Mode         RunPlanMode
	ResumeFrom   string
	ParentTaskID *int64
	Nodes        map[string]*NodePlan
	TopoOrder    []string
}

type RunPlanMode

type RunPlanMode string
const (
	RunPlanModeInitial RunPlanMode = "initial"
	RunPlanModeFork    RunPlanMode = "fork"
)

type RunRedoService

type RunRedoService interface {
	RedoRun(
		ctx context.Context,
		sourceTaskID int64,
		resumeSpec *domain.ResumeSpec,
		overrideInput map[string]any,
		editAction string,
		editLabel string,
		note string,
	) (*domain.Task, error)
}

type RunResult

type RunResult struct {
	Status RunStatus
	Err    error

	// 可选
	SuspendReason string
	SuspendNode   string
}

type RunStatus

type RunStatus string
const (
	RunSuccess   RunStatus = "success"
	RunSuspended RunStatus = "suspended"
	RunFailed    RunStatus = "failed"
	RunNoop      RunStatus = "noop"
)

type TaskForkService

type TaskForkService struct {
	// contains filtered or unexported fields
}

func NewTaskForkService

func NewTaskForkService(
	taskRepo repository.TaskRepository,
	workflowVersionRepo repository.WorkflowVersionRepository,
	builder *workflow.Builder,
	eng *Engine,
) *TaskForkService

func (*TaskForkService) RedoRun

func (s *TaskForkService) RedoRun(
	ctx context.Context,
	sourceTaskID int64,
	resumeSpec *domain.ResumeSpec,
	overrideInput map[string]any,
	editAction string,
	editLabel string,
	note string,
) (*domain.Task, error)

type TaskRetryService

type TaskRetryService interface {
	PrepareTaskRetry(ctx context.Context, taskID int64, trigger RetryTrigger, resumeFrom string, patches []domain.RuntimePatch) error
	ClearMapChildCheckpointResult(ctx context.Context, parentTaskID int64, nodeName string, childTaskID int64) error
}

func NewTaskRetryService

func NewTaskRetryService(
	workflowVersionRepo repository.WorkflowVersionRepository,
	taskRepo repository.TaskRepository,
	nodeRuntimeRepo repository.NodeRuntimeRepository,
	awaitBindingRepo repository.AwaitBindingRepository,
	builder *workflow.Builder,
) TaskRetryService

Directories

Path Synopsis
Package redisjobqueue provides a Redis Stream backed engine.AsyncJobQueue.
Package redisjobqueue provides a Redis Stream backed engine.AsyncJobQueue.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL