feat(backend): batch & loop node fix get input
This commit is contained in:
parent
249c23c64b
commit
31621542c8
|
|
@ -3604,6 +3604,55 @@ func TestNodeDebugLoop(t *testing.T) {
|
||||||
result = r.getNodeExeHistory(id, "", "wrong_node_id", ptr.Of(workflow.NodeHistoryScene_TestRunInput))
|
result = r.getNodeExeHistory(id, "", "wrong_node_id", ptr.Of(workflow.NodeHistoryScene_TestRunInput))
|
||||||
assert.Equal(t, "", result.Output)
|
assert.Equal(t, "", result.Output)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
mockey.PatchConvey("test node debug loop", t, func() {
|
||||||
|
r := newWfTestRunner(t)
|
||||||
|
defer r.closeFn()
|
||||||
|
|
||||||
|
id := r.load("loop_selector_variable_assign_text_processor.json")
|
||||||
|
exeID := r.nodeDebug(id, "192046", withNDInput(map[string]string{"input": `["a", "bb", "ccc", "dddd"]`}))
|
||||||
|
e := r.getProcess(id, exeID, withSpecificNodeID("192046"))
|
||||||
|
e.assertSuccess()
|
||||||
|
assert.Equal(t, map[string]any{
|
||||||
|
"converted": []any{
|
||||||
|
"new_a",
|
||||||
|
"new_ccc",
|
||||||
|
},
|
||||||
|
"variable_out": "dddd",
|
||||||
|
}, mustUnmarshalToMap(t, e.output))
|
||||||
|
|
||||||
|
result := r.getNodeExeHistory(id, exeID, "192046", nil)
|
||||||
|
assert.Equal(t, mustUnmarshalToMap(t, e.output), mustUnmarshalToMap(t, result.Output))
|
||||||
|
|
||||||
|
// verify this workflow has not been successfully test ran
|
||||||
|
result = r.getNodeExeHistory(id, "", "100001", ptr.Of(workflow.NodeHistoryScene_TestRunInput))
|
||||||
|
assert.Equal(t, "", result.Output)
|
||||||
|
|
||||||
|
// verify that another node of this workflow is not node debugged
|
||||||
|
result = r.getNodeExeHistory(id, "", "wrong_node_id", ptr.Of(workflow.NodeHistoryScene_TestRunInput))
|
||||||
|
assert.Equal(t, "", result.Output)
|
||||||
|
})
|
||||||
|
|
||||||
|
mockey.PatchConvey("test node debug loop", t, func() {
|
||||||
|
r := newWfTestRunner(t)
|
||||||
|
defer r.closeFn()
|
||||||
|
runner := mockcode.NewMockRunner(r.ctrl)
|
||||||
|
runner.EXPECT().Run(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, request *code.RunRequest) (*code.RunResponse, error) {
|
||||||
|
return &code.RunResponse{
|
||||||
|
Result: request.Params,
|
||||||
|
}, nil
|
||||||
|
}).AnyTimes()
|
||||||
|
|
||||||
|
code.SetCodeRunner(runner)
|
||||||
|
id := r.load("loop_with_object_input.json")
|
||||||
|
exeID := r.nodeDebug(id, "122149",
|
||||||
|
withNDInput(map[string]string{"input": `[{"a":"1"},{"a":"2"}]`}))
|
||||||
|
e := r.getProcess(id, exeID, withSpecificNodeID("122149"))
|
||||||
|
e.assertSuccess()
|
||||||
|
assert.Equal(t, `{"output":["1","2"]}`, e.output)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCopyWorkflow(t *testing.T) {
|
func TestCopyWorkflow(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,254 @@
|
||||||
|
{
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": "100001",
|
||||||
|
"type": "1",
|
||||||
|
"meta": {
|
||||||
|
"position": {
|
||||||
|
"x": 180,
|
||||||
|
"y": 26.000000000000007
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"nodeMeta": {
|
||||||
|
"description": "工作流的起始节点,用于设定启动工作流需要的信息",
|
||||||
|
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Start-v2.jpg",
|
||||||
|
"subTitle": "",
|
||||||
|
"title": "开始"
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"type": "list",
|
||||||
|
"name": "objs",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"schema": []
|
||||||
|
},
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"trigger_parameters": [
|
||||||
|
{
|
||||||
|
"type": "list",
|
||||||
|
"name": "objs",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"schema": []
|
||||||
|
},
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "900001",
|
||||||
|
"type": "2",
|
||||||
|
"meta": {
|
||||||
|
"position": {
|
||||||
|
"x": 1300,
|
||||||
|
"y": 13.000000000000007
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"nodeMeta": {
|
||||||
|
"description": "工作流的最终节点,用于返回工作流运行后的结果信息",
|
||||||
|
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-End-v2.jpg",
|
||||||
|
"subTitle": "",
|
||||||
|
"title": "结束"
|
||||||
|
},
|
||||||
|
"inputs": {
|
||||||
|
"terminatePlan": "returnVariables",
|
||||||
|
"inputParameters": [
|
||||||
|
{
|
||||||
|
"name": "output",
|
||||||
|
"input": {
|
||||||
|
"type": "list",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"type": "ref",
|
||||||
|
"content": {
|
||||||
|
"source": "block-output",
|
||||||
|
"blockID": "122149",
|
||||||
|
"name": "output"
|
||||||
|
},
|
||||||
|
"rawMeta": {
|
||||||
|
"type": 99
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "122149",
|
||||||
|
"type": "21",
|
||||||
|
"meta": {
|
||||||
|
"position": {
|
||||||
|
"x": 740,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"canvasPosition": {
|
||||||
|
"x": 560,
|
||||||
|
"y": 317.30000000000007
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"nodeMeta": {
|
||||||
|
"title": "循环",
|
||||||
|
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Loop-v2.jpg",
|
||||||
|
"description": "用于通过设定循环次数和逻辑,重复执行一系列任务",
|
||||||
|
"mainColor": "#00B2B2",
|
||||||
|
"subTitle": "循环"
|
||||||
|
},
|
||||||
|
"inputs": {
|
||||||
|
"loopType": "array",
|
||||||
|
"loopCount": {
|
||||||
|
"type": "integer",
|
||||||
|
"value": {
|
||||||
|
"type": "literal",
|
||||||
|
"content": "10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"variableParameters": [],
|
||||||
|
"inputParameters": [
|
||||||
|
{
|
||||||
|
"name": "input",
|
||||||
|
"input": {
|
||||||
|
"type": "list",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"schema": []
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"type": "ref",
|
||||||
|
"content": {
|
||||||
|
"source": "block-output",
|
||||||
|
"blockID": "100001",
|
||||||
|
"name": "objs"
|
||||||
|
},
|
||||||
|
"rawMeta": {
|
||||||
|
"type": 103
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "output",
|
||||||
|
"input": {
|
||||||
|
"type": "list",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"type": "ref",
|
||||||
|
"content": {
|
||||||
|
"source": "block-output",
|
||||||
|
"blockID": "116706",
|
||||||
|
"name": "input.a"
|
||||||
|
},
|
||||||
|
"rawMeta": {
|
||||||
|
"type": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"id": "116706",
|
||||||
|
"type": "5",
|
||||||
|
"meta": {
|
||||||
|
"position": {
|
||||||
|
"x": 180,
|
||||||
|
"y": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"nodeMeta": {
|
||||||
|
"title": "代码",
|
||||||
|
"icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Code-v2.jpg",
|
||||||
|
"description": "编写代码,处理输入变量来生成返回值",
|
||||||
|
"mainColor": "#00B2B2",
|
||||||
|
"subTitle": "代码"
|
||||||
|
},
|
||||||
|
"inputs": {
|
||||||
|
"inputParameters": [
|
||||||
|
{
|
||||||
|
"name": "input",
|
||||||
|
"input": {
|
||||||
|
"type": "object",
|
||||||
|
"schema": [],
|
||||||
|
"value": {
|
||||||
|
"type": "ref",
|
||||||
|
"content": {
|
||||||
|
"source": "block-output",
|
||||||
|
"blockID": "122149",
|
||||||
|
"name": "input"
|
||||||
|
},
|
||||||
|
"rawMeta": {
|
||||||
|
"type": 6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"code": "# 在这里,您可以通过 'args' 获取节点中的输入变量,并通过 'ret' 输出结果\n# 'args' 已经被正确地注入到环境中\n# 下面是一个示例,首先获取节点的全部输入参数params,其次获取其中参数名为'input'的值:\n# params = args.params; \n# input = params['input'];\n# 下面是一个示例,输出一个包含多种数据类型的 'ret' 对象:\n# ret: Output = { \"name\": '小明', \"hobbies\": [\"看书\", \"旅游\"] };\nimport json\nasync def main(args: Args) -> Output:\n params = args.params\n return params",
|
||||||
|
"language": 3,
|
||||||
|
"settingOnError": {
|
||||||
|
"processType": 1,
|
||||||
|
"timeoutMs": 60000,
|
||||||
|
"retryTimes": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"name": "input",
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "a"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"edges": [
|
||||||
|
{
|
||||||
|
"sourceNodeID": "122149",
|
||||||
|
"targetNodeID": "116706",
|
||||||
|
"sourcePortID": "loop-function-inline-output"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceNodeID": "116706",
|
||||||
|
"targetNodeID": "122149",
|
||||||
|
"targetPortID": "loop-function-inline-input"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"edges": [
|
||||||
|
{
|
||||||
|
"sourceNodeID": "100001",
|
||||||
|
"targetNodeID": "122149"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceNodeID": "122149",
|
||||||
|
"targetNodeID": "900001",
|
||||||
|
"sourcePortID": "loop-output"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"versions": {
|
||||||
|
"loop": "v2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -169,21 +169,16 @@ func (b *Batch) Execute(ctx context.Context, in map[string]any, opts ...nodes.Ne
|
||||||
currentKey := string(b.config.BatchNodeKey) + "#" + arrayKey
|
currentKey := string(b.config.BatchNodeKey) + "#" + arrayKey
|
||||||
|
|
||||||
// Recursively expand map[string]any elements
|
// Recursively expand map[string]any elements
|
||||||
if m, ok := ele.(map[string]any); ok {
|
|
||||||
var expand func(prefix string, val interface{})
|
var expand func(prefix string, val interface{})
|
||||||
expand = func(prefix string, val interface{}) {
|
expand = func(prefix string, val interface{}) {
|
||||||
|
input[prefix] = val
|
||||||
if nestedMap, ok := val.(map[string]any); ok {
|
if nestedMap, ok := val.(map[string]any); ok {
|
||||||
for k, v := range nestedMap {
|
for k, v := range nestedMap {
|
||||||
expand(prefix+"#"+k, v)
|
expand(prefix+"#"+k, v)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
input[prefix] = val
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
expand(currentKey, m)
|
expand(currentKey, ele)
|
||||||
} else {
|
|
||||||
input[currentKey] = ele
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return input, items, nil
|
return input, items, nil
|
||||||
|
|
|
||||||
|
|
@ -199,21 +199,16 @@ func (l *Loop) Execute(ctx context.Context, in map[string]any, opts ...nodes.Nes
|
||||||
currentKey := string(l.config.LoopNodeKey) + "#" + arrayKey
|
currentKey := string(l.config.LoopNodeKey) + "#" + arrayKey
|
||||||
|
|
||||||
// Recursively expand map[string]any elements
|
// Recursively expand map[string]any elements
|
||||||
if m, ok := ele.(map[string]any); ok {
|
|
||||||
var expand func(prefix string, val interface{})
|
var expand func(prefix string, val interface{})
|
||||||
expand = func(prefix string, val interface{}) {
|
expand = func(prefix string, val interface{}) {
|
||||||
|
input[prefix] = val
|
||||||
if nestedMap, ok := val.(map[string]any); ok {
|
if nestedMap, ok := val.(map[string]any); ok {
|
||||||
for k, v := range nestedMap {
|
for k, v := range nestedMap {
|
||||||
expand(prefix+"#"+k, v)
|
expand(prefix+"#"+k, v)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
input[prefix] = val
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
expand(currentKey, m)
|
expand(currentKey, ele)
|
||||||
} else {
|
|
||||||
input[currentKey] = ele
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return input, items, nil
|
return input, items, nil
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue