runtime

package
v1.0.6 Latest Latest
Warning

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

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

Documentation

Overview

Package runtime 是 Flux 的执行底座:只认"机制"(依赖/并行/异步/状态/复用/分叉), 不认识 workflow DSL。它不 import definition、不 import expr。

对应关系:本文件取代 engine/run_plan.go 的 RunPlan/NodePlan, 把 NodePlan.NodeType (definition.NodeType) 退化为 Async / Join 两个运行时原语。

Index

Constants

View Source
const AsyncHelloNode = "async_hello"
View Source
const EchoNode = "echo"

Variables

View Source
var ErrDeadlock = errors.New("runtime: no runnable node and not done (dependency deadlock)")
View Source
var ErrMaxSteps = errors.New("runtime: max steps exceeded")

Functions

func FanNodeName

func FanNodeName(i int) string

FanNodeName 返回 fanout 中第 i 个分支的节点名(暴露给测试)。

Types

type AwaitController

type AwaitController interface {
	// Begin 为 async 节点登记一个等待(提交外部请求、写 AwaitBinding)。
	// 返回后节点进入 NodeAwaiting,由外部事件/poll 通过 Scheduler.Resume 唤醒。
	Begin(ctx context.Context, node *PlanNode, input map[string]any) (bindingID int64, err error)
}

AwaitController 异步原语:提交外部任务 + 建 AwaitBinding,等待外部唤醒。 现有 engine 的 executeAwaitNode / CompleteAwaitNode + AwaitBindingRepository 适配它。

type Emitter

type Emitter interface {
	Emit(event Event)
}

Emitter 事件端口(现有 nodes.Context.EmitToolEvent 适配它)。

type Event

type Event struct {
	Node     string
	Type     string
	Message  string
	Progress float64
	Data     map[string]any
}

type ExecState

type ExecState interface {
	Input() map[string]any // 任务级输入
	Output(node string) map[string]any
	SetOutput(node string, out map[string]any)

	State(node string) NodeState
	Transition(node string, to NodeState)

	// Nodes 返回当前已知节点名(动态前沿下会随 planner 增长)
	Nodes() []string
}

ExecState 是底座读写的"活"执行上下文。 现有的 workflow/nodes.Context(持 Output/Runtime + expr 环境)将实现这个接口, 从而老路径无需改数据结构即可跑在新底座上。

type FileAwait

type FileAwait struct {
	Dir string
	// contains filtered or unexported fields
}

FileAwait 实现 AwaitController 端口:在内存中管理 binding,同时持久化到文件。 B-M1b 验证用:crash 后从 binding 文件恢复。

func NewFileAwait

func NewFileAwait(dir string) *FileAwait

func (*FileAwait) Begin

func (fa *FileAwait) Begin(_ context.Context, node *PlanNode, input map[string]any) (int64, error)

func (*FileAwait) BindingIDs

func (fa *FileAwait) BindingIDs() []int64

BindingIDs 返回所有活跃 binding ID(crash 恢复后用于扫描)。

func (*FileAwait) Complete

func (fa *FileAwait) Complete(bindingID int64) (nodeName string, input map[string]any, ok bool)

Complete 模拟外部回调完成:返回 nodeName 和 input(验证用)。

type FileStore

type FileStore struct {
	Dir   string
	State *MemState // 共享引用——Scheduler 和 Store 操作同一份 MemState
	// contains filtered or unexported fields
}

FileStore 实现 Store 端口,每次 PersistNode 时把完整 ExecState 快照写到文件。 B-M1b 验证用:crash 后从快照恢复状态。

func NewFileStore

func NewFileStore(dir string, state *MemState) *FileStore

func (*FileStore) PersistNode

func (fs *FileStore) PersistNode(_ context.Context, _ string, _ NodeState, _ map[string]any) error

type InputResolver

type InputResolver func(ctx context.Context, state ExecState) (map[string]any, error)

InputResolver:给定当前执行状态,产出本节点入参。失败即节点失败。

type Invoker

type Invoker interface {
	Invoke(ctx context.Context, toolName string, input map[string]any, emit Emitter) (map[string]any, error)
}

Invoker 执行一次工具调用。tool.Registry 适配它(Tool-First)。

type JoinKind

type JoinKind uint8
const (
	JoinAll JoinKind = iota // 所有依赖 success 才就绪(默认)
	JoinAny                 // 任一依赖 success 即就绪(条件分支 / merge)
)

type MemState

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

MemState 是 ExecState 的最小内存实现,供 demo / planner / 测试复用(纯 stdlib)。 生产环境中 nodes.Context 也实现 ExecState(见 workflow 包的适配)。

func LoadSnapshot

func LoadSnapshot(path string) (*MemState, error)

LoadSnapshot 从文件恢复完整执行状态(进程重启后的第一条指令)。

func NewMemState

func NewMemState(input map[string]any) *MemState

func (*MemState) Input

func (m *MemState) Input() map[string]any

func (*MemState) Nodes

func (m *MemState) Nodes() []string

func (*MemState) Output

func (m *MemState) Output(node string) map[string]any

func (*MemState) SaveSnapshot

func (m *MemState) SaveSnapshot(path string) error

SaveSnapshot 把完整执行状态写入文件(crash 前最后一道防线)。

func (*MemState) SetOutput

func (m *MemState) SetOutput(node string, o map[string]any)

func (*MemState) State

func (m *MemState) State(node string) NodeState

func (*MemState) Transition

func (m *MemState) Transition(node string, to NodeState)

type NodeState

type NodeState uint8

NodeState 运行时节点状态(自包含,不依赖 domain)。 适配器层负责与 domain.NodeRuntime 的状态互转。

const (
	NodePending NodeState = iota
	NodeRunning
	NodeAwaiting // 已挂起等外部事件(对应 domain.NodeAwaiting)
	NodeSuccess
	NodeFailed
	NodeSkipped
)

func (NodeState) Terminal

func (s NodeState) Terminal() bool

type NopEmitter

type NopEmitter struct{}

NopEmitter 是无操作的事件发射器(B-M1b 这类同步验证不需要 trace)。

func (NopEmitter) Emit

func (NopEmitter) Emit(Event)

type Plan

type Plan struct {
	Nodes map[string]*PlanNode
}

Plan 是一张"已解析的可执行 DAG"。谁产出的不重要:

  • workflow 编译器把人写的 WorkflowDefinition 编译成 Plan
  • planner 把 Goal 增量生成成 Plan
  • SDK/代码直接拼 Plan

func FanoutPlan

func FanoutPlan(n int) *Plan

FanoutPlan 构造 N 个并行的 async 节点 + 一个 join 节点。

DAG: [async_0, async_1, ..., async_N-1] → join
所有 async 节点无依赖(并行提交),join 依赖所有 async(JoinAll)。
join 的 Resolve 收集所有分支的输出(包括失败分支——通过 state 检查)。

func SimplePlan

func SimplePlan() *Plan

SimplePlan 构造 B-M1b 验证用的 async_hello → echo 计划。

type PlanNode

type PlanNode struct {
	Name      string
	ToolName  string   // 节点要调用的工具(registry 里查)
	DependsOn []string // 纯拓扑依赖边(不含条件)

	// Async 取代 definition 的 await/async node type。
	// true → 走"挂起 + AwaitBinding + 外部唤醒"路径(对应 executor.go:133)。
	Async bool

	// Join 决定依赖满足语义:map/loop 的并行汇聚下沉成的原语。
	Join JoinKind

	// Resolve 在执行到本节点时、用实时上游产出算出本节点入参。
	// workflow 编译器 → expr 求值实现;planner → 直接给值。expr-lang 因此被关在前端。
	Resolve InputResolver

	Retry RetryPolicy
}

PlanNode 一个可执行单元 = 一次工具调用(Tool-First)。 注意这里没有 input_mapping 表达式、没有 edge condition —— 那些是编排语义, 已在前端编译期解析掉,只留下纯依赖边 + 一个 InputResolver 回调。

type PlanSource

type PlanSource interface {
	// Next 返回新增(追加到计划中)的节点。底座负责依赖求解与调度,
	// 因此 Source 只需"产出节点",不需要自己算就绪顺序。
	Next(ctx context.Context, state ExecState) (added []*PlanNode, done bool, err error)
}

PlanSource 是底座的"计划来源"。这是让 workflow 与 planner 可互换的关键抽象:

  • 静态 workflow:首次返回整张 DAG,done=true(退化情形)
  • planner/agent:每次根据已完成结果返回下一步,done 由 LLM 判定(增量规划)

func FanoutPlanSource

func FanoutPlanSource(n int) PlanSource

FanoutPlanSource 把 FanoutPlan 包成一次性 StaticSource。

func SimplePlanSource

func SimplePlanSource() PlanSource

SimplePlanSource 把 SimplePlan 包成一次性 StaticSource。

type ProviderAwait

type ProviderAwait struct {
	Dir         string
	ProviderURL string
	HTTPClient  *http.Client
	// contains filtered or unexported fields
}

ProviderAwait 实现 AwaitController,对真实 HTTP Provider 做 submit。 Begin() → POST /submit → 拿到 provider_task_id → 存入 binding。 crash 恢复时从文件重建 binding 映射。

func NewProviderAwait

func NewProviderAwait(dir, providerURL string) *ProviderAwait

NewProviderAwait 创建 ProviderAwait。从文件恢复已有 binding(crash 恢复)。

func (*ProviderAwait) Begin

func (pa *ProviderAwait) Begin(_ context.Context, node *PlanNode, input map[string]any) (int64, error)

Begin 向 Provider 提交异步任务,创建 binding。

func (*ProviderAwait) BindingIDs

func (pa *ProviderAwait) BindingIDs() []int64

BindingIDs 返回所有活跃 binding ID(crash 恢复后用于扫描未完成的 provider 任务)。

func (*ProviderAwait) NodeForBinding

func (pa *ProviderAwait) NodeForBinding(bindingID int64) (string, bool)

NodeForBinding 返回 binding 对应的节点名(供 Resume 用)。

func (*ProviderAwait) PollProvider

func (pa *ProviderAwait) PollProvider(taskID string) (status string, result map[string]any, err error)

PollProvider 轮询 Provider 指定任务的状态。返回 (status, result, error)。

func (*ProviderAwait) ProviderTaskID

func (pa *ProviderAwait) ProviderTaskID(bindingID int64) (string, bool)

ProviderTaskID 返回 binding 对应的 provider task_id(供轮询用)。

type Result

type Result struct {
	Status RunStatus
	Err    error
}

type RetryPolicy

type RetryPolicy struct {
	MaxRetries int
	Interval   time.Duration
}

type RunStatus

type RunStatus uint8
const (
	StatusCompleted RunStatus = iota
	StatusSuspended           // 有节点在 NodeAwaiting,等外部唤醒后再 Resume
	StatusFailed
)

type Scheduler

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

Scheduler 是底座的执行器:消费 PlanSource,做依赖求解,跑就绪节点, 处理 sync 内联 / async 挂起。它取代 engine.runDAG 的拓扑循环,但不认识 definition。

func NewScheduler

func NewScheduler(inv Invoker, aw AwaitController, st Store, em Emitter) *Scheduler

func (*Scheduler) Resume

func (s *Scheduler) Resume(ctx context.Context, src PlanSource, state ExecState, node string, out map[string]any) (Result, error)

Resume 由外部完成事件触发(webhook/poll → CompleteAwaitNode 的 runtime 版)。 把 async 节点置为 success 并写回产出,然后重入 Run 继续推进 DAG。

func (*Scheduler) Run

func (s *Scheduler) Run(ctx context.Context, src PlanSource, state ExecState) (Result, error)

Run 驱动一次(可恢复的)执行。返回 Suspended 时,外部事件到达后调用 Resume 再次进入。

func (*Scheduler) WithMaxSteps

func (s *Scheduler) WithMaxSteps(n int) *Scheduler

WithMaxSteps 设置 Run 单次循环的硬上限(FR6:control loop 的停机保证, 独立于 planner 的 done——防 LLM 永不终止 / 震荡)。超限返回 ErrMaxSteps。0=不限制。 additive:不改 NewScheduler/Run 签名。

func (*Scheduler) WithPlan

func (s *Scheduler) WithPlan(p *Plan) *Scheduler

WithPlan 预载入已编译好的计划(crash 恢复用)。 恢复后的 scheduler 已有完整 plan,Run/Resume 时 src.Next 产出的已知节点会被跳过, 从而不会把已恢复的 NodeSuccess 覆写成 NodePending。 additive:不改 NewScheduler/Run 签名。

func (*Scheduler) WithTrace

func (s *Scheduler) WithTrace(sink TraceSink, runID string) *Scheduler

WithTrace 启用 sidecar trace(Phase 1)。additive:不改 NewScheduler/Run 签名。 trace 只记录、不驱动任何决策——移除 sink 不改变执行结果。

type StaticSource

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

StaticSource 把一张完整的 Plan 包成"一次性给完"的 Source —— 这就是让 老 workflow 路径在新底座上原样跑通的垫片(shim)。

用法:plan := workflow.Compile(def); scheduler.Run(ctx, runtime.NewStaticSource(plan), state)

func NewStaticSource

func NewStaticSource(plan *Plan) *StaticSource

func (*StaticSource) Next

func (s *StaticSource) Next(_ context.Context, _ ExecState) ([]*PlanNode, bool, error)

type Store

type Store interface {
	PersistNode(ctx context.Context, node string, state NodeState, out map[string]any) error
}

Store 持久化端口:节点状态/输出落库(现有 NodeRuntime 持久化适配它)。

type TraceClass

type TraceClass uint8

TraceClass 区分两条语义流,但二者共享同一条 Seq 全序。

const (
	// ClassExecution 确定性流:节点生命周期 + tool I/O。
	ClassExecution TraceClass = iota
	// ClassControl 非确定性流:planner/编译器的计划产出(plan_extend)。
	// "agent 的自由"是内核外部性,一旦记录就被固化为确定性事实。
	ClassControl
)

type TraceEvent

type TraceEvent struct {
	RunID string
	// Seq 是全序真相:execution 与 control 共用同一单调序列,
	// 跨流因果("先有 Y.output,planner 才产出 X")才可回放。回放靠 Seq,不靠 wall-clock。
	Seq     int64
	Class   TraceClass
	Node    string // plan_extend 时为空
	Type    TraceType
	Payload map[string]any
}

TraceEvent 一条不可变的执行真相记录。

type TraceSink

type TraceSink interface {
	EmitExecution(e TraceEvent)
	EmitControl(e TraceEvent)
}

TraceSink 双写协议。两个方法只是语义分类;Seq 由唯一写入者(Scheduler)统一盖章, 保证两流共享单序列。nil sink ⇒ 完全不记录(sidecar 默认关闭,决策无副作用)。

type TraceType

type TraceType string
const (
	TraceNodeStart  TraceType = "node_start"
	TraceInput      TraceType = "input"  // resolved input(回放时的确定性入参)
	TraceOutput     TraceType = "output" // tool 产出(回放时要注入的外部不确定性)
	TraceAwait      TraceType = "await"
	TraceResume     TraceType = "resume"
	TraceFail       TraceType = "fail"
	TracePlanExtend TraceType = "plan_extend" // control 流:planner 产出了哪些节点
)

Directories

Path Synopsis
Package mockprovider 提供可控的异步任务模拟 HTTP 服务,供 B-M1 验证用。
Package mockprovider 提供可控的异步任务模拟 HTTP 服务,供 B-M1 验证用。

Jump to

Keyboard shortcuts

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