Files
ragflow/internal/agent/canvas/canvas.go

86 lines
3.5 KiB
Go
Raw Normal View History

feat(agent): Go port — canvas engine, 22 components, DSL v2, 13 endpoints (#15952) Ports the agent canvas subsystem from Python to Go. ## What's included ### Canvas Engine (Phase 0/1) - State engine, scheduler, variable resolver, Redis checkpoint store, cancel protocol - **209 tests** across canvas / component / io packages ### 22 Components (P0–P4) | Tier | Components | |---|---| | P0 T1+T2+T3 | LLM, Agent, ExitLoop, Switch, Categorize, Begin, Message, Invoke | | P1 T3 | VariableAggregator, VariableAssigner, StringTransform, ListOperations, DataOperations | | P2 T3 | Iteration, IterationItem, Loop, LoopItem | | P3 T3 | UserFillUp, Fillup | | P4 T5 | Browser, ExcelProcessor, DocsGenerator | ### DSL v2 Schema (Phase 2.5) - Typed v2 in-memory model with v1-to-v2 auto-detect converter - v1 legacy field stripping per plan §2.11.7 ### HTTP Endpoints & Bug Fixes (Plans PR1–PR3) - **DELETE SQL bug fix**: gorm v2 `Where("id = ?", id).Delete(...)` pattern - **CreateAgent validation**: title/DSL required, duplicate check, 103 envelope - **13 new endpoints**: templates, prompts, tags, sessions CRUD, chat/completions (SSE + non-stream stubs), rerun, test_db_connection, logs, webhook/logs - **756 Go unit tests** (745 → 756, +18) - **17 → 0 Python integration test failures** (test_agents.py + test_session_management/) ### Tools 21 eino tools: HTTPHelper, search tools, financial/data tools, mandatory stubs ### Infrastructure OTel observability, NATS message queue, DeepDoc gRPC client, SSRF guards, IDOR mitigation
2026-06-12 22:58:28 +08:00
// Package canvas implements the RAGFlow agent canvas Go port.
// See plan: .claude/plans/agent-go-port.md §2.5 (State + Workflow hybrid),
// §2.6 (Redis-backed CheckPointStore + RunTracker), §4.2 (CanvasState shape).
//
// Shared runtime contracts (CanvasState, Component, ComponentFactory,
// state context plumbing, template helpers) live in
// internal/agent/runtime. Canvas re-exports them through thin aliases
// so existing call sites keep working while breaking the historic
// canvas <-> component import cycle.
package canvas
import (
"ragflow/internal/agent/runtime"
)
// legacyNoOpNames is the set of component names that the Go port
// recognises for DSL v1 compatibility but does not ship a real
// implementation for. Encountering one of these in a DSL is mapped to
// the same no-op echo lambda used for placeholder bodies by the
// BuildWorkflow in scheduler.go. New DSLs should not use these names —
// they exist only so v1 DSLs that reference Python-era sentinel
// components ("ExitLoop") still compile and run in the Go port.
//
// Membership semantics inside a Loop's sub-graph: legacy names that
// appear as descendants of a Loop are absorbed as no-op members of the
// sub-graph; they do not contribute to loop control. Termination is
// driven by the Loop's loop_termination_condition predicate, not by
// reaching an ExitLoop node.
var legacyNoOpNames = map[string]bool{
"exitloop": true,
}
// CanvasState aliases runtime.CanvasState so existing canvas callers
// (and component tests that still import the canvas package) keep
// compiling without changes. The canonical definition lives in
// internal/agent/runtime/state.go.
type CanvasState = runtime.CanvasState
// NewCanvasState re-exports runtime.NewCanvasState.
func NewCanvasState(runID, taskID string) *CanvasState {
return runtime.NewCanvasState(runID, taskID)
}
// Canvas is the in-memory DSL representation loaded from a user_canvas row.
// It is the input to compile.go which builds the eino Workflow.
type Canvas struct {
Version int `json:"version"`
Components map[string]CanvasComponent `json:"components"`
Path []string `json:"path"`
History []map[string]any `json:"history,omitempty"`
Retrieval map[string]any `json:"retrieval,omitempty"`
Globals map[string]any `json:"globals,omitempty"`
}
// CanvasComponent is the v1-shape component node (Phase 1 uses v1; v2 lands
// in Phase 2.5 per plan §2.5.3 and §5).
//
// The Obj.ComponentName matches agent/component/<name>.py's class name
// (case-insensitive per dsl-v1-corner-cases.md §13).
type CanvasComponent struct {
Obj CanvasComponentObj `json:"obj"`
Downstream []string `json:"downstream"`
Upstream []string `json:"upstream"`
}
type CanvasComponentObj struct {
ComponentName string `json:"component_name"`
Params map[string]any `json:"params"`
}
// Component is an alias for runtime.Component — the minimal runtime
// surface BuildWorkflow needs at sub-graph build time. The canonical
// definition (and the SetDefaultFactory / DefaultFactory plumbing)
// lives in internal/agent/runtime/component.go.
type Component = runtime.Component
// ComponentFactory aliases runtime.ComponentFactory.
type ComponentFactory = runtime.ComponentFactory
// SetDefaultFactory re-exports runtime.SetDefaultFactory. The
// orchestrator's main.go can call either entry point; new code
// should prefer the runtime package directly.
func SetDefaultFactory(f ComponentFactory) {
runtime.SetDefaultFactory(f)
}