feat: 支持 Python 代码节点 main 返回输出
- 自动传入节点输入并映射 main 返回字典,兼容历史 _result - 限制脚本输出缓冲并补充运行时与编辑器测试 - 同步 Python 帮助、占位提示和独立可执行补全片段
This commit is contained in:
@@ -31,6 +31,106 @@ public class PythonRuntimeEngineTest {
|
||||
Assert.assertEquals("dict", result.get("env_type"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteMainSuccess() {
|
||||
Assume.assumeTrue(PythonRuntimeEngine.probe(PYTHON_COMMAND, 1500L).isAvailable());
|
||||
|
||||
Chain chain = createChain();
|
||||
CodeNode node = (CodeNode) chain.getDefinition().getNodeById("code-test");
|
||||
chain.getState().getMemory().put("ambient", "memory-value");
|
||||
PythonRuntimeEngine engine = new PythonRuntimeEngine(
|
||||
PYTHON_COMMAND,
|
||||
3000L,
|
||||
65536,
|
||||
System.getProperty("java.io.tmpdir"));
|
||||
|
||||
Map<String, Object> result = engine.execute(
|
||||
String.join("\n",
|
||||
"import math",
|
||||
"def main(inputs):",
|
||||
" return {",
|
||||
" 'answer': inputs['input'],",
|
||||
" 'score': 95,",
|
||||
" 'sqrt': math.sqrt(16),",
|
||||
" 'has_ambient': 'ambient' in inputs",
|
||||
" }"),
|
||||
node,
|
||||
chain,
|
||||
Map.of("input", "hello"));
|
||||
|
||||
Assert.assertEquals("hello", result.get("answer"));
|
||||
Assert.assertEquals(95, ((Number) result.get("score")).intValue());
|
||||
Assert.assertEquals(4D, ((Number) result.get("sqrt")).doubleValue(), 0D);
|
||||
Assert.assertEquals(Boolean.FALSE, result.get("has_ambient"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLegacyResultTakesPrecedenceOverMain() {
|
||||
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",
|
||||
"_result['source'] = 'legacy'",
|
||||
"def main(inputs):",
|
||||
" return {'source': 'main'}"),
|
||||
node,
|
||||
chain,
|
||||
Map.of("input", "hello"));
|
||||
|
||||
Assert.assertEquals("legacy", result.get("source"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMainMustReturnDict() {
|
||||
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"));
|
||||
|
||||
assertExecuteFail(
|
||||
engine,
|
||||
node,
|
||||
chain,
|
||||
"def main(inputs):\n return 'invalid'",
|
||||
"Python main 函数必须返回 dict");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCapturedOutputIsBounded() {
|
||||
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,
|
||||
64,
|
||||
System.getProperty("java.io.tmpdir"));
|
||||
|
||||
Map<String, Object> result = engine.execute(
|
||||
String.join("\n",
|
||||
"import sys",
|
||||
"print('a' * 2048)",
|
||||
"_result['truncated'] = sys.stdout.truncated"),
|
||||
node,
|
||||
chain);
|
||||
|
||||
Assert.assertEquals(Boolean.TRUE, result.get("truncated"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSyntaxError() {
|
||||
Assume.assumeTrue(PythonRuntimeEngine.probe(PYTHON_COMMAND, 1500L).isAvailable());
|
||||
|
||||
Reference in New Issue
Block a user