Files
ragflow/internal/agent/component/registry.go
Zhichang Yu e45659868a feat(agent): ship the Go agent canvas port — eino interrupt/resume + Redis check-pointing (#16035)
Replaces the Python agent canvas runtime with a Go implementation that
runs inside `cmd/server_main`.

The canvas compiles into an eino Workflow that pauses on wait-for-user
via native Interrupt/Resume (no sentinel flag) and resumes from a
Redis-backed CheckPointStore.

All 21 Python agent components and ~35 tools are ported with functional
parity.

Sandbox providers now read their JSON config from the admin-panel
system_settings table with env fallback.

234 files / +35,413 / -6,111. All Go files are gofmt-clean (CI gate
added); drops the v2 DSL E2E step and the gap-analysis plan (both
redundant after the port ships).

## Type of change

- [x] Refactoring
- [x] New feature
- [x] Bug fix

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-17 13:24:03 +08:00

77 lines
2.3 KiB
Go

// Package component — registry (orchestrator-owned, DO NOT EDIT).
//
// Registry maps component names to factories. Each component's init() calls
// Register(name, factory) to enroll itself; lookup is case-insensitive
// (matches Python v1 component_name case-insensitivity).
package component
import (
"fmt"
"strings"
"sync"
)
// Factory constructs a Component from a params map (loaded from the DSL).
// Returning an error here aborts the run with a clear message.
type Factory func(params map[string]any) (Component, error)
var (
registryMu sync.RWMutex
registry = make(map[string]Factory)
)
// Register enrolls a component factory under name (case-insensitive).
// Intended to be called from init() in each component's <name>.go file.
func Register(name string, f Factory) {
registryMu.Lock()
defer registryMu.Unlock()
key := strings.ToLower(strings.TrimSpace(name))
if key == "" {
panic("component: Register called with empty name")
}
if _, exists := registry[key]; exists {
panic(fmt.Sprintf("component: %q already registered", name))
}
registry[key] = f
}
// New constructs a Component by name. Returns an error if the name is
// unknown or the factory rejects the params. The empty-string case is
// treated as "not found" so the error message is consistent.
func New(name string, params map[string]any) (Component, error) {
registryMu.RLock()
f, ok := registry[strings.ToLower(strings.TrimSpace(name))]
registryMu.RUnlock()
if !ok {
return nil, fmt.Errorf("component: unknown component %q (registered: %s)", name, RegisteredNames())
}
if f == nil {
return nil, fmt.Errorf("component: nil factory for %q", name)
}
return f(params)
}
// RegisteredNames returns the sorted list of registered component names.
// Used for diagnostics and the API 500 path "list available components".
func RegisteredNames() []string {
registryMu.RLock()
defer registryMu.RUnlock()
names := make([]string, 0, len(registry))
for n := range registry {
names = append(names, n)
}
// Stable order for error messages / UI listing.
sortStrings(names)
return names
}
// sortStrings is a small in-place insertion sort to avoid the sort package
// dependency for a list that's <50 items long in practice.
func sortStrings(s []string) {
for i := 1; i < len(s); i++ {
for j := i; j > 0 && s[j-1] > s[j]; j-- {
s[j-1], s[j] = s[j], s[j-1]
}
}
}