Files
ragflow/internal/agent/canvas/parallel_timing_test.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

89 lines
3.7 KiB
Go

//
// Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// This file documents the canvas parallel-execution defense:
//
// 1. Structural compile-check (this file,
// TestCanvas_ParallelExecution_StaticAnalysis). Verifies the
// canvas compile produces an eino Workflow whose declared
// edges form a valid DAG — a precondition for eino's
// topological-wave parallel execution.
//
// 2. eino's own parallel-execution test in
// `internal/agent/canvas/parallel_batch_test.go`
// (TestBuildWorkflow_ParallelBatchStructure). Verifies the
// canvas-to-eino compile path with a 4-node sibling topology.
//
// Plus: eino's own `taskManager.submit` test in
// `go/pkg/mod/github.com/cloudwego/eino@v0.9.4/compose/graph_manager_test.go`
// covers the per-task `go func` parallel execution path.
//
// eino's compose.Workflow.Run spawns one `go t.execute()` goroutine
// per ready node in each topological wave, so the parallel-execution
// behavior is intrinsic to using eino — no Go port work needed
// beyond correct `AddInput` edge wiring, which the canvas scheduler
// already does.
package canvas
import (
"context"
"testing"
)
// TestCanvas_ParallelExecution_StaticAnalysis verifies the canvas
// compile produces an eino Workflow whose declared edges form a
// valid DAG (no cycles) — a precondition for eino's topological-
// wave parallel execution. A cycle in the eino DAG would surface
// as a graph-cycle error from Compile; a missing `AddInput` edge
// would surface as a different DAG topology than what the user
// expects at runtime.
//
// Together with `parallel_batch_test.go::TestBuildWorkflow_ParallelBatchStructure`
// (4-node sibling structural test) and eino's own `taskManager` tests,
// this gives the regression defense for the claim that
// "compose.Workflow natively executes independent nodes in a layer
// concurrently".
func TestCanvas_ParallelExecution_StaticAnalysis(t *testing.T) {
// 5 nodes: begin → {a, b, c} (parallel wave) → final.
// `a`, `b`, `c` are independent siblings — they have no
// inter-dependency. eino's compile() will see all three
// ready in the same topological wave.
c := &Canvas{
Components: map[string]CanvasComponent{
"begin": {Obj: CanvasComponentObj{ComponentName: "Begin"}, Downstream: []string{"a", "b", "c"}},
"a": {Obj: CanvasComponentObj{ComponentName: "Message"}, Downstream: []string{"final"}},
"b": {Obj: CanvasComponentObj{ComponentName: "Message"}, Downstream: []string{"final"}},
"c": {Obj: CanvasComponentObj{ComponentName: "Message"}, Downstream: []string{"final"}},
"final": {Obj: CanvasComponentObj{ComponentName: "Message"}},
},
Path: []string{"begin", "a", "b", "c", "final"},
}
cc, err := Compile(context.Background(), c)
if err != nil {
t.Fatalf("Compile: %v", err)
}
if cc == nil {
t.Fatal("Compile returned nil CompiledCanvas")
}
// The 3-sibling topology (a, b, c) compiled without
// errors. eino's topological sort ran in Compile; if we
// had a cycle (e.g., c's downstream pointing back to
// begin), Compile would fail with a graph-cycle error.
// The fact that it succeeded is the structural proof.
}