From 654279a745b0016a111a0855934934cd126260e6 Mon Sep 17 00:00:00 2001 From: Haruko386 Date: Wed, 15 Jul 2026 10:55:39 +0800 Subject: [PATCH] fix: support code_execute for go backend (#16903) ### Summary As title image --- .../component/production_chain_fixes_test.go | 46 +++++++++++++++++++ .../agent/component/universe_a_wrappers.go | 35 ++++++++++---- internal/agent/sandbox/self_managed.go | 2 +- 3 files changed, 73 insertions(+), 10 deletions(-) diff --git a/internal/agent/component/production_chain_fixes_test.go b/internal/agent/component/production_chain_fixes_test.go index 840b095560..4b2796e206 100644 --- a/internal/agent/component/production_chain_fixes_test.go +++ b/internal/agent/component/production_chain_fixes_test.go @@ -626,6 +626,52 @@ func TestCodeExec_LegacyDSLWrapperResolvesArgumentRefsFromState(t *testing.T) { } } +func TestCodeExec_LegacyDSLWrapperResolvesSysArgumentRefsFromCanvasState(t *testing.T) { + prev := agenttool.GetSandboxClient() + recorder := &codeExecSandboxRecorder{ + resp: &agenttool.SandboxResponse{ + ExitCode: 0, + StructuredResult: map[string]any{ + "present": true, + "value": "532", + }, + }, + } + agenttool.SetSandboxClient(recorder) + t.Cleanup(func() { agenttool.SetSandboxClient(prev) }) + + c, err := New(componentNameCodeExec, map[string]any{ + "lang": "python", + "script": "def main(num):\n" + + " return num\n", + "arguments": map[string]any{ + "num": "sys.query", + }, + "outputs": map[string]any{ + "result": map[string]any{ + "type": "String", + }, + }, + }) + if err != nil { + t.Fatalf("New(CodeExec): %v", err) + } + + state := runtime.NewCanvasState("run-codeexec", "task-codeexec") + state.Sys["query"] = "532" + ctx := runtime.WithState(context.Background(), state) + out, err := c.Invoke(ctx, nil) + if err != nil { + t.Fatalf("CodeExec.Invoke: %v", err) + } + if got := recorder.req.Arguments["num"]; got != "532" { + t.Fatalf("sandbox arguments[num] = %#v, want \"532\"", got) + } + if got := out["result"]; got != "532" { + t.Fatalf("CodeExec result = %v, want 532", got) + } +} + func TestCodeExec_LegacyDSLWrapperContractMismatchSetsError(t *testing.T) { prev := agenttool.GetSandboxClient() recorder := &codeExecSandboxRecorder{ diff --git a/internal/agent/component/universe_a_wrappers.go b/internal/agent/component/universe_a_wrappers.go index 56645d6baf..15c3bac30a 100644 --- a/internal/agent/component/universe_a_wrappers.go +++ b/internal/agent/component/universe_a_wrappers.go @@ -14,7 +14,7 @@ // limitations under the License. // -// Universe A delegation wrappers. Canvas-facing components that +// Package component Universe A delegation wrappers. Canvas-facing components that // delegate to their corresponding Universe B eino tool // implementations. The delegation pattern keeps the canvas // scheduler's Component contract thin and the eino tool's @@ -1496,6 +1496,17 @@ func (c *codeExecComponent) Inputs() map[string]string { } } +func (c *codeExecComponent) GetInputForm() map[string]any { + res := make(map[string]any, len(c.params)) + for k, _ := range c.params { + res[k] = map[string]any{ + "type": "line", + "name": k, + } + } + return res +} + func (c *codeExecComponent) Outputs() map[string]string { return map[string]string{ "result": "The main(...) return value rendered as the legacy CodeExec result field.", @@ -1517,7 +1528,8 @@ func (c *codeExecComponent) Invoke(ctx context.Context, inputs map[string]any) ( merged[k] = v } if rawArgs, ok := merged["arguments"].(map[string]any); ok { - merged["arguments"] = resolveCodeExecArguments(rawArgs, merged) + state, _, _ := runtime.GetStateFromContext[*runtime.CanvasState](ctx) + merged["arguments"] = resolveCodeExecArguments(rawArgs, merged, state) } common.Debug("CodeExec wrapper invoke", zap.Int("params_keys", len(c.params)), @@ -1656,29 +1668,29 @@ func cloneAnyMap(in map[string]any) map[string]any { return out } -func resolveCodeExecArguments(args map[string]any, merged map[string]any) map[string]any { +func resolveCodeExecArguments(args map[string]any, merged map[string]any, state *runtime.CanvasState) map[string]any { if args == nil { return nil } out := make(map[string]any, len(args)) for k, v := range args { - out[k] = resolveCodeExecArgumentValue(v, merged) + out[k] = resolveCodeExecArgumentValue(v, merged, state) } return out } -func resolveCodeExecArgumentValue(v any, merged map[string]any) any { +func resolveCodeExecArgumentValue(v any, merged map[string]any, state *runtime.CanvasState) any { switch x := v.(type) { case map[string]any: - return resolveCodeExecArguments(x, merged) + return resolveCodeExecArguments(x, merged, state) case []any: out := make([]any, 0, len(x)) for _, item := range x { - out = append(out, resolveCodeExecArgumentValue(item, merged)) + out = append(out, resolveCodeExecArgumentValue(item, merged, state)) } return out case string: - if resolved, ok := lookupCodeExecArgumentRef(x, merged); ok { + if resolved, ok := lookupCodeExecArgumentRef(x, merged, state); ok { return resolved } return x @@ -1687,11 +1699,16 @@ func resolveCodeExecArgumentValue(v any, merged map[string]any) any { } } -func lookupCodeExecArgumentRef(ref string, merged map[string]any) (any, bool) { +func lookupCodeExecArgumentRef(ref string, merged map[string]any, state *runtime.CanvasState) (any, bool) { ref = strings.TrimSpace(ref) if ref == "" { return nil, false } + if state != nil { + if v, err := state.GetVar(ref); err == nil && v != nil { + return v, true + } + } at := strings.Index(ref, "@") if at <= 0 || at >= len(ref)-1 { return nil, false diff --git a/internal/agent/sandbox/self_managed.go b/internal/agent/sandbox/self_managed.go index 2ae90f4e4e..10bc60a03a 100644 --- a/internal/agent/sandbox/self_managed.go +++ b/internal/agent/sandbox/self_managed.go @@ -75,7 +75,7 @@ import ( // selfManagedDefaultEndpoint is the canonical executor_manager // endpoint baked into the Python side. Operators override via // SANDBOX_EXECUTOR_MANAGER_URL. -const selfManagedDefaultEndpoint = "http://sandbox-executor-manager:9385" +const selfManagedDefaultEndpoint = "http://localhost:9385" // SelfManagedProvider is the Go port of // `agent/sandbox/providers/self_managed.py::SelfManagedProvider`.