feat: 默认省略代码节点显式 main 调用

- 新节点仅生成 main 与 return 并同步输入形参

- Python 运行时按配置顺序传入独立参数值

- 保留历史显式调用和单对象参数写法
This commit is contained in:
2026-08-03 16:58:20 +08:00
parent 46398d1365
commit a75ae9a843
7 changed files with 232 additions and 51 deletions

View File

@@ -2,12 +2,14 @@ package tech.easyflow.ai.easyagentsflow.code;
import com.easyagents.flow.core.chain.Chain;
import com.easyagents.flow.core.chain.ChainDefinition;
import com.easyagents.flow.core.chain.Parameter;
import com.easyagents.flow.core.chain.repository.InMemoryChainStateRepository;
import com.easyagents.flow.core.node.CodeNode;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Test;
import java.util.Arrays;
import java.util.Map;
import java.util.UUID;
@@ -64,6 +66,38 @@ public class PythonRuntimeEngineTest {
Assert.assertEquals(Boolean.FALSE, result.get("has_ambient"));
}
/**
* 验证新节点按配置顺序把输入值传给 main 的独立形参。
*/
@Test
public void testExecuteMainWithNamedArguments() {
Assume.assumeTrue(PythonRuntimeEngine.probe(PYTHON_COMMAND, 1500L).isAvailable());
Chain chain = createChain();
CodeNode node = (CodeNode) chain.getDefinition().getNodeById("code-test");
node.setParameters(Arrays.asList(
new Parameter("data"),
new Parameter("suffix")));
node.setMainArgsMode(CodeNode.MAIN_ARGS_MODE_NAMED);
PythonRuntimeEngine engine = new PythonRuntimeEngine(
PYTHON_COMMAND,
3000L,
65536,
System.getProperty("java.io.tmpdir"));
Map<String, Object> result = engine.execute(
String.join("\n",
"def main(data, suffix):",
" return {'answer': data + suffix}"),
node,
chain,
Map.of(
"suffix", "111",
"data", "hello"));
Assert.assertEquals("hello111", result.get("answer"));
}
@Test
public void testLegacyResultTakesPrecedenceOverMain() {
Assume.assumeTrue(PythonRuntimeEngine.probe(PYTHON_COMMAND, 1500L).isAvailable());
@@ -88,6 +122,35 @@ public class PythonRuntimeEngineTest {
Assert.assertEquals("legacy", result.get("source"));
}
/**
* 验证显式调用返回空字典时不会再次自动调用 main。
*/
@Test
public void testEmptyExplicitResultDoesNotInvokeMainAgain() {
Assume.assumeTrue(PythonRuntimeEngine.probe(PYTHON_COMMAND, 1500L).isAvailable());
Chain chain = createChain();
CodeNode node = (CodeNode) chain.getDefinition().getNodeById("code-test");
PythonRuntimeEngine engine = new PythonRuntimeEngine(
PYTHON_COMMAND,
3000L,
65536,
System.getProperty("java.io.tmpdir"));
Map<String, Object> result = engine.execute(
String.join("\n",
"def main(data):",
" if not isinstance(data, str):",
" raise RuntimeError('main invoked twice')",
" return {}",
"_result = main(data)"),
node,
chain,
Map.of("data", "hello"));
Assert.assertTrue(result.isEmpty());
}
@Test
public void testMainMustReturnDict() {
Assume.assumeTrue(PythonRuntimeEngine.probe(PYTHON_COMMAND, 1500L).isAvailable());