Why: the completion doc Q2 requires twelve builtin topologies each with a scripted-backend conformance; today the engine has only the standard/leader mode string with no topology abstraction, no loop re-arm (cycles are fail-closed by design), no in-graph routing, and no run deadline. Evidence: GOWORK=off go vet + go test -count=1 green across squad and all three adapter modules; ten conformance/validation tests: sequential, parallel, loop-until-field (exact iteration count + persisted LoopRuns), loop-budget-exhausted (loop_exhausted event, run still completes), router-selects-branch (loser skipped, run NOT degraded), router-no-match fail-closed, evaluator-optimizer (draft re-armed via loop body until verdict=pass), long-running deadline (pending steps skipped, degraded closeout), unknown/primitive/ unregistered-driver fail-closed, builtin registry completeness (12). Impact: additive — Topology/Deadline on RunOptions, Topology on Blueprint, Loop/LoopRuns/Branches/Handoff on StepSpec; empty topology keeps byte-identical standard behavior; MaxIterations is the total iteration cap (first run included); router-skipped steps are exempt from degraded derivation; runParallel now preserves the first specific RunFailed error instead of re-wrapping via failStep; deadline metadata uses RFC3339Nano (second-precision RFC3339 truncated sub-second deadlines into instant expiry). Refs: Agent-System-Documents#44 (Q2 plan WP1)
131 lines
4.3 KiB
Go
131 lines
4.3 KiB
Go
package squad
|
||
|
||
import "fmt"
|
||
|
||
// topology.go — 拓扑一等化(Q2):内置拓扑名注册表。
|
||
// 拓扑不新造执行面:graph 类编译为 Steps 图(Q1 机制),leader 走既有
|
||
// Mode 委派循环,driver 类由引擎按名分发(review/group_chat/task_board),
|
||
// primitive 类是跨拓扑原语(handoff/mailbox),不可单独作为运行拓扑。
|
||
// 未知名与不可运行Kind一律 fail-closed。
|
||
|
||
// 内置拓扑名(completion doc Q2 清单的 12 种)。
|
||
const (
|
||
TopologySequential = "sequential"
|
||
TopologyParallel = "parallel"
|
||
TopologyLoop = "loop"
|
||
TopologyRouter = "router"
|
||
TopologyLeader = "leader"
|
||
TopologyHandoff = "handoff"
|
||
TopologyGroupChat = "group_chat"
|
||
TopologyReview = "review"
|
||
TopologyEvaluatorOptimizer = "evaluator_optimizer"
|
||
TopologyLongRunning = "long_running"
|
||
TopologyTaskBoard = "task_board"
|
||
TopologyMailbox = "mailbox"
|
||
)
|
||
|
||
// TopologyKind 是拓扑的执行形态。
|
||
type TopologyKind string
|
||
|
||
const (
|
||
// TopologyGraph 编译为 Steps 图,由 flowDriver.advance 执行。
|
||
TopologyGraph TopologyKind = "graph"
|
||
// TopologyMode 走既有 Mode 分支(leader 委派循环)。
|
||
TopologyMode TopologyKind = "mode"
|
||
// TopologyDriver 由引擎按名分发到专用驱动。
|
||
TopologyDriver TopologyKind = "driver"
|
||
// TopologyPrimitive 是跨拓扑原语,不可单独运行。
|
||
TopologyPrimitive TopologyKind = "primitive"
|
||
)
|
||
|
||
// TopologySpec 是一个内置拓扑的注册项。
|
||
type TopologySpec struct {
|
||
Name string
|
||
Kind TopologyKind
|
||
}
|
||
|
||
// builtinTopologies 是 12 种内置拓扑的注册表(唯一权威清单)。
|
||
var builtinTopologies = []TopologySpec{
|
||
{Name: TopologySequential, Kind: TopologyGraph},
|
||
{Name: TopologyParallel, Kind: TopologyGraph},
|
||
{Name: TopologyLoop, Kind: TopologyGraph},
|
||
{Name: TopologyRouter, Kind: TopologyGraph},
|
||
{Name: TopologyLeader, Kind: TopologyMode},
|
||
{Name: TopologyHandoff, Kind: TopologyPrimitive},
|
||
{Name: TopologyGroupChat, Kind: TopologyDriver},
|
||
{Name: TopologyReview, Kind: TopologyDriver},
|
||
{Name: TopologyEvaluatorOptimizer, Kind: TopologyGraph},
|
||
{Name: TopologyLongRunning, Kind: TopologyGraph},
|
||
{Name: TopologyTaskBoard, Kind: TopologyDriver},
|
||
{Name: TopologyMailbox, Kind: TopologyPrimitive},
|
||
}
|
||
|
||
// BuiltinTopologies 返回内置拓扑清单(只读副本)。
|
||
func BuiltinTopologies() []TopologySpec {
|
||
out := make([]TopologySpec, len(builtinTopologies))
|
||
copy(out, builtinTopologies)
|
||
return out
|
||
}
|
||
|
||
// LookupTopology 按名查注册项。
|
||
func LookupTopology(name string) (TopologySpec, bool) {
|
||
for _, t := range builtinTopologies {
|
||
if t.Name == name {
|
||
return t, true
|
||
}
|
||
}
|
||
return TopologySpec{}, false
|
||
}
|
||
|
||
// registeredDrivers 是已接线的 driver 拓扑(WP 增量逐个点亮;未点亮 = fail-closed)。
|
||
var registeredDrivers = map[string]bool{}
|
||
|
||
// RegisterTopologyDriver 由驱动实现方在 init/装配时点亮一个 driver 拓扑。
|
||
func RegisterTopologyDriver(name string) error {
|
||
spec, ok := LookupTopology(name)
|
||
if !ok {
|
||
return fmt.Errorf("topology: 未注册的拓扑名 %q", name)
|
||
}
|
||
if spec.Kind != TopologyDriver {
|
||
return fmt.Errorf("topology: %q 不是 driver 类(kind=%s)", name, spec.Kind)
|
||
}
|
||
registeredDrivers[name] = true
|
||
return nil
|
||
}
|
||
|
||
// checkTopology 校验一次运行的有效拓扑名,返回其 Kind。
|
||
// 空名 = 未声明(standard 图行为,兼容既有调用方)。
|
||
// 未注册名 / primitive 单跑 / driver 未点亮都 fail-closed。
|
||
func checkTopology(name string) (TopologyKind, error) {
|
||
if name == "" {
|
||
return TopologyGraph, nil
|
||
}
|
||
spec, ok := LookupTopology(name)
|
||
if !ok {
|
||
return "", fmt.Errorf("topology: 未知拓扑 %q(内置清单:%s)", name, topologyNames())
|
||
}
|
||
switch spec.Kind {
|
||
case TopologyGraph, TopologyMode:
|
||
return spec.Kind, nil
|
||
case TopologyPrimitive:
|
||
return "", fmt.Errorf("topology: %q 是跨拓扑原语,不可单独作为运行拓扑", name)
|
||
case TopologyDriver:
|
||
if !registeredDrivers[name] {
|
||
return "", fmt.Errorf("topology: %q 驱动未注册(fail-closed)", name)
|
||
}
|
||
return spec.Kind, nil
|
||
}
|
||
return "", fmt.Errorf("topology: %q kind 非法", name)
|
||
}
|
||
|
||
func topologyNames() string {
|
||
s := ""
|
||
for i, t := range builtinTopologies {
|
||
if i > 0 {
|
||
s += ", "
|
||
}
|
||
s += t.Name
|
||
}
|
||
return s
|
||
}
|