Files
Agent-System-Backend/internal/workflow/registry.go
T
weichen f1302ed5b3 Enable canonical versioned Go module consumption
Why: Allow consumers to download an immutable module revision without nested source checkouts or local path replacements.

Evidence: SELF package tests and the prior canonical-path rejection are recorded in the Dev dependency-workspace task card; remote consumption is replayed against the committed candidate.

Impact: Change the module identity and its Go imports while preserving API behavior. Existing historical submodule revisions remain buildable.

Refs: Versioned dependencies U1/U3; feature/dependency-workspace
2026-10-13 15:47:16 +00:00

108 lines
2.8 KiB
Go

// Package workflow 保存已装载的工作流定义。它只依赖 core,执行器在 runtime。
package workflow
import (
"encoding/json"
"sync"
core "git-ssh.u1680832.nyat.app/weichen/Agent-System-Core.git"
)
// ScriptKey 是脚本正文的索引。同一个相对路径可以出现在不同工作流里。
func ScriptKey(workflowID, path string) string {
return workflowID + "\n" + path
}
// Registry 是当前装载集合的快照。Set 整体替换,不增量合并。
type Registry struct {
mu sync.RWMutex
byID map[string]core.WorkflowDef
byIntent map[string]string
scripts map[string]string
}
func NewRegistry() *Registry {
return &Registry{
byID: map[string]core.WorkflowDef{},
byIntent: map[string]string{},
scripts: map[string]string{},
}
}
// Set 用这一批定义替换注册表。同 ID 或同意图以后出现的为准。
// scripts 的键是 ScriptKey。定义会被拷贝,调用方之后的修改不影响已注册的副本。
func (r *Registry) Set(defs []core.WorkflowDef, scripts map[string]string) {
byID := map[string]core.WorkflowDef{}
byIntent := map[string]string{}
for _, def := range defs {
cloned := cloneDef(def)
byID[cloned.ID] = cloned
if cloned.IntentID != "" {
byIntent[cloned.IntentID] = cloned.ID
}
}
copied := map[string]string{}
for k, v := range scripts {
copied[k] = v
}
r.mu.Lock()
r.byID, r.byIntent, r.scripts = byID, byIntent, copied
r.mu.Unlock()
}
func (r *Registry) Get(id string) (core.WorkflowDef, bool) {
r.mu.RLock()
defer r.mu.RUnlock()
def, ok := r.byID[id]
if !ok {
return core.WorkflowDef{}, false
}
return cloneDef(def), true
}
func (r *Registry) ByIntent(intentID string) (core.WorkflowDef, bool) {
r.mu.RLock()
defer r.mu.RUnlock()
id, ok := r.byIntent[intentID]
if !ok {
return core.WorkflowDef{}, false
}
return cloneDef(r.byID[id]), true
}
func (r *Registry) Script(workflowID, path string) (string, bool) {
r.mu.RLock()
defer r.mu.RUnlock()
body, ok := r.scripts[ScriptKey(workflowID, path)]
return body, ok
}
// SlotQuestion 是启动工作流时还没填上的必填槽位。
type SlotQuestion struct {
Key string
Question string
}
// Launch 是一次工作流 run 启动时的副本。之后插件卸载不能改到它。
type Launch struct {
Def core.WorkflowDef
Slots map[string]string
Missing []SlotQuestion
Scripts map[string]string // 键是步骤里的相对路径,值是当时读到的脚本正文
}
// Clone 拷贝定义,调用方之后的修改不影响副本。
func Clone(def core.WorkflowDef) core.WorkflowDef { return cloneDef(def) }
func cloneDef(def core.WorkflowDef) core.WorkflowDef {
raw, err := json.Marshal(def)
if err != nil {
return def
}
var out core.WorkflowDef
if err := json.Unmarshal(raw, &out); err != nil {
return def
}
return out
}