Port agent PRs to GO - 2 (#16565)

### Summary

Port the following PRs to GO in this PR

https://github.com/infiniflow/ragflow/pull/16420
https://github.com/infiniflow/ragflow/pull/13295
This commit is contained in:
qinling0210
2026-07-02 20:20:11 +08:00
committed by GitHub
parent 24118ac0d1
commit dcbd0d260c
8 changed files with 524 additions and 11 deletions

View File

@@ -9,6 +9,9 @@ package canvas
import (
"ragflow/internal/agent/runtime"
"ragflow/internal/common"
"go.uber.org/zap"
)
// legacyNoOpNames is the set of component names that the Go port
@@ -69,6 +72,64 @@ type CanvasComponentObj struct {
Params map[string]any `json:"params"`
}
// Close releases resources held by components referenced in the canvas
// DSL. It walks every component's params map and calls Close() on any
// value that implements a Close() method (MCPToolAdapters, HTTP
// clients, etc.). Mirrors Python's Graph.close() in agent/canvas.py.
//
// In Go's architecture MCP sessions are per-invocation and auto-torn
// down; Close() is a best-effort hook that ensures idle HTTP
// connections are released even when adapters outlive a single call.
func (c *Canvas) Close() {
if c == nil {
return
}
seen := make(map[any]bool)
for _, comp := range c.Components {
for _, v := range comp.Obj.Params {
walkAndClose(v, seen)
}
}
}
// walkAndClose recursively walks a value and calls Close() on any
// objects that implement a Close() method. Maps, slices, and pointers
// are recursed into; other types are skipped. Already-seen objects
// (by interface identity) are skipped to avoid double-close.
func walkAndClose(v any, seen map[any]bool) {
if v == nil {
return
}
if closer, ok := v.(interface{ Close() }); ok {
if !seen[closer] {
seen[closer] = true
safeClose(closer)
}
return
}
switch val := v.(type) {
case map[string]any:
for _, child := range val {
walkAndClose(child, seen)
}
case []any:
for _, child := range val {
walkAndClose(child, seen)
}
}
}
// safeClose calls Close() on a closer value, swallowing panics so a
// misbehaving resource doesn't crash the canvas tear-down path.
func safeClose(closer interface{ Close() }) {
defer func() {
if rec := recover(); rec != nil {
common.Warn("canvas: Close() panicked", zap.Any("recover", rec))
}
}()
closer.Close()
}
// 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)

View File

@@ -152,6 +152,19 @@ func (m *MCPToolAdapter) InvokableRun(ctx context.Context, argumentsInJSON strin
return res.Text, nil
}
// Close releases resources held by the adapter. In Go's architecture
// MCP sessions are per-invocation (created and torn down within each
// InvokableRun call), so there are no persistent connections to drain.
// The primary resource is the http.Client's idle-connection pool;
// calling Close explicitly drops those idle connections so they don't
// accumulate across many adapter instances over long-running processes.
// Mirrors Python's close_sync() in common/mcp_tool_call_conn.py.
func (m *MCPToolAdapter) Close() {
if m.httpClient != nil {
m.httpClient.CloseIdleConnections()
}
}
// BuildMCPToolAdapters wraps a slice of mcpclient.Tool descriptors as
// eino InvokableTool. Returned slice is suitable for handing to
// agenttool.NewRetrieverTool / NewMCPToolAdapter paths or directly to