feat: 整体项目架构完成, 运行的核心算法完成, 自定义节点starter以外定义节点测试通过
This commit is contained in:
@@ -0,0 +1,10 @@
|
|||||||
|
package com.metisapp.custom;
|
||||||
|
|
||||||
|
import com.metis.domain.entity.base.NodeConfig;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class CustomTestConfig extends NodeConfig {
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.metisapp.custom;
|
||||||
|
|
||||||
|
import com.metis.domain.context.RunningContext;
|
||||||
|
import com.metis.domain.context.RunningResult;
|
||||||
|
import com.metis.domain.entity.base.Edge;
|
||||||
|
import com.metis.domain.entity.base.Node;
|
||||||
|
import com.metis.runner.CustomNodeRunner;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class CustomTestRunner implements CustomNodeRunner<CustomTestConfig> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCustomNodeType() {
|
||||||
|
return "test";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RunningResult run(RunningContext context, Node node, List<Edge> edges) {
|
||||||
|
log.info("自定义节点测试");
|
||||||
|
return RunningResult.buildResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.metisapp.custom;
|
||||||
|
|
||||||
|
import com.metis.domain.entity.base.Edge;
|
||||||
|
import com.metis.domain.entity.base.Node;
|
||||||
|
import com.metis.validator.CustomNodeValidator;
|
||||||
|
import com.metis.validator.ValidatorResult;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class CustomTestValidator implements CustomNodeValidator<CustomTestConfig> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCustomNodeType() {
|
||||||
|
return "test";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValidatorResult validateValue(Node node) {
|
||||||
|
CustomTestConfig config = node.getConfig();
|
||||||
|
return ValidatorResult.valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValidatorResult validateRelation(Node node, List<Edge> sources, List<Edge> targets) {
|
||||||
|
CustomTestConfig config = node.getConfig();
|
||||||
|
return ValidatorResult.valid();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
"id": 0,
|
"id": 0,
|
||||||
"name": "测试流程",
|
"name": "测试流程",
|
||||||
@@ -8,145 +7,561 @@
|
|||||||
{
|
{
|
||||||
"id": "5",
|
"id": "5",
|
||||||
"type": "start",
|
"type": "start",
|
||||||
"dimensions": {
|
"initialized": false,
|
||||||
"width": 300,
|
"position": {
|
||||||
"height": 300
|
"x": -81.81250000000003,
|
||||||
|
"y": 275.49609375
|
||||||
},
|
},
|
||||||
"draggable": true,
|
|
||||||
"resizing": false,
|
|
||||||
"selected": true,
|
|
||||||
"data": {
|
"data": {
|
||||||
"label": "开始",
|
"label": "开始",
|
||||||
|
"icon": "SuitcaseLine",
|
||||||
"toolbarPosition": "right",
|
"toolbarPosition": "right",
|
||||||
|
"description": "开始述描述",
|
||||||
"config": {
|
"config": {
|
||||||
"variables": [
|
|
||||||
{
|
|
||||||
"variable": "context",
|
|
||||||
"label": "段落",
|
|
||||||
"type": "paragraph",
|
|
||||||
"maxLength": 48,
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"variable": "text",
|
|
||||||
"label": "文本",
|
|
||||||
"type": "text-input",
|
|
||||||
"maxLength": 48,
|
|
||||||
"required": true,
|
|
||||||
"options": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"variable": "select",
|
|
||||||
"label": "下拉",
|
|
||||||
"type": "select",
|
|
||||||
"maxLength": 48,
|
|
||||||
"required": true,
|
|
||||||
"options": [
|
|
||||||
{
|
|
||||||
"label": "选型1",
|
|
||||||
"value": "1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "选型2",
|
|
||||||
"value": "2"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"variable": "number",
|
|
||||||
"label": "数字",
|
|
||||||
"type": "number",
|
|
||||||
"maxLength": 48,
|
|
||||||
"required": true,
|
|
||||||
"options": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"variable": "singlefile",
|
|
||||||
"label": "singlefile单文件",
|
|
||||||
"type": "file",
|
|
||||||
"maxLength": 48,
|
|
||||||
"required": true,
|
|
||||||
"options": [],
|
|
||||||
"allowedFileUploadMethods": [
|
|
||||||
"local_file",
|
|
||||||
"remote_url"
|
|
||||||
],
|
|
||||||
"allowedFileTypes": [
|
|
||||||
"image",
|
|
||||||
"document",
|
|
||||||
"audio",
|
|
||||||
"video"
|
|
||||||
],
|
|
||||||
"allowedFileExtensions": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"variable": "mufile",
|
|
||||||
"label": "多文件",
|
|
||||||
"type": "file-list",
|
|
||||||
"maxLength": 5,
|
|
||||||
"required": true,
|
|
||||||
"options": [],
|
|
||||||
"allowedFileUploadMethods": [
|
|
||||||
"local_file"
|
|
||||||
],
|
|
||||||
"allowedFileTypes": [
|
|
||||||
"custom"
|
|
||||||
],
|
|
||||||
"allowedFileExtensions": [
|
|
||||||
"docx",
|
|
||||||
"aaa"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"parent": "234"
|
"parent": "234"
|
||||||
},
|
},
|
||||||
"handles": [
|
"handles": [
|
||||||
{
|
{
|
||||||
"id": "7",
|
"id": "51",
|
||||||
"position": "right",
|
"position": "right",
|
||||||
"type": "source",
|
"type": "source",
|
||||||
"connectable": true
|
"connectable": true
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"item": {
|
||||||
|
"id": "2",
|
||||||
|
"type": "run",
|
||||||
|
"labelType": "knowledge",
|
||||||
|
"label": "知识检索"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"position": { "x": 0, "y": 300 }
|
"width": 200,
|
||||||
|
"height": 40
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "6",
|
"id": "6",
|
||||||
"type": "end",
|
"type": "end",
|
||||||
"selected": false,
|
"initialized": false,
|
||||||
|
"position": {
|
||||||
|
"x": 1281.582055572882,
|
||||||
|
"y": 236.2912067630247
|
||||||
|
},
|
||||||
"data": {
|
"data": {
|
||||||
"label": "结束",
|
"label": "结束",
|
||||||
"toolbarPosition": "right",
|
"toolbarPosition": "right",
|
||||||
"handles": [
|
"handles": [
|
||||||
{
|
{
|
||||||
"id": "8",
|
"id": "61",
|
||||||
"position": "left",
|
"position": "left",
|
||||||
"type": "target",
|
"type": "target",
|
||||||
"connectable": true
|
"connectable": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"position": { "x": 500, "y": 300 }
|
"width": 200,
|
||||||
|
"height": 40
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "188",
|
||||||
|
"type": "custom",
|
||||||
|
"customType": "test",
|
||||||
|
"initialized": false,
|
||||||
|
"position": {
|
||||||
|
"x": 265.87532955148635,
|
||||||
|
"y": 71.40983063296031
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"label": "llm1",
|
||||||
|
"icon": "",
|
||||||
|
"description": "llm描述描述",
|
||||||
|
"toolbarPosition": "right",
|
||||||
|
"config": {
|
||||||
|
"labelType": "llm"
|
||||||
|
},
|
||||||
|
"handles": [
|
||||||
|
{
|
||||||
|
"id": "11",
|
||||||
|
"position": "left",
|
||||||
|
"type": "source",
|
||||||
|
"connectable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "45",
|
||||||
|
"position": "right",
|
||||||
|
"type": "source",
|
||||||
|
"connectable": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"width": 200,
|
||||||
|
"height": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "850",
|
||||||
|
"type": "llm",
|
||||||
|
"initialized": false,
|
||||||
|
"position": {
|
||||||
|
"x": 269.7896091295129,
|
||||||
|
"y": 253.80570624004747
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"label": "llm2",
|
||||||
|
"icon": "",
|
||||||
|
"description": "llm描述描述",
|
||||||
|
"toolbarPosition": "right",
|
||||||
|
"config": {
|
||||||
|
"labelType": "llm"
|
||||||
|
},
|
||||||
|
"handles": [
|
||||||
|
{
|
||||||
|
"id": "43",
|
||||||
|
"position": "left",
|
||||||
|
"type": "source",
|
||||||
|
"connectable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "57",
|
||||||
|
"position": "right",
|
||||||
|
"type": "source",
|
||||||
|
"connectable": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"width": 200,
|
||||||
|
"height": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "979",
|
||||||
|
"type": "llm",
|
||||||
|
"initialized": false,
|
||||||
|
"position": {
|
||||||
|
"x": 257.0893454883237,
|
||||||
|
"y": 360
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"label": "llm3",
|
||||||
|
"icon": "",
|
||||||
|
"description": "llm描述描述",
|
||||||
|
"toolbarPosition": "right",
|
||||||
|
"config": {
|
||||||
|
"labelType": "llm"
|
||||||
|
},
|
||||||
|
"handles": [
|
||||||
|
{
|
||||||
|
"id": "40",
|
||||||
|
"position": "left",
|
||||||
|
"type": "source",
|
||||||
|
"connectable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "46",
|
||||||
|
"position": "right",
|
||||||
|
"type": "source",
|
||||||
|
"connectable": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"item": {
|
||||||
|
"id": "1",
|
||||||
|
"type": "run",
|
||||||
|
"labelType": "llm",
|
||||||
|
"label": "llm"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"width": 200,
|
||||||
|
"height": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "818",
|
||||||
|
"type": "questionClassifier",
|
||||||
|
"initialized": false,
|
||||||
|
"position": {
|
||||||
|
"x": 247.56414775743187,
|
||||||
|
"y": 467.44099824508874
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"label": "知识检索条件",
|
||||||
|
"icon": "",
|
||||||
|
"description": "知识检索描述描述",
|
||||||
|
"toolbarPosition": "right",
|
||||||
|
"config": {
|
||||||
|
"labelType": "knowledge"
|
||||||
|
},
|
||||||
|
"handles": [
|
||||||
|
{
|
||||||
|
"id": "86",
|
||||||
|
"position": "left",
|
||||||
|
"type": "source",
|
||||||
|
"connectable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "11",
|
||||||
|
"position": "right",
|
||||||
|
"type": "source",
|
||||||
|
"connectable": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"item": {
|
||||||
|
"id": "1",
|
||||||
|
"type": "run",
|
||||||
|
"labelType": "llm",
|
||||||
|
"label": "llm"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"width": 200,
|
||||||
|
"height": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "288",
|
||||||
|
"type": "llm",
|
||||||
|
"initialized": false,
|
||||||
|
"position": {
|
||||||
|
"x": 545.6891477574318,
|
||||||
|
"y": 431.4042601585389
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"label": "llm5",
|
||||||
|
"icon": "",
|
||||||
|
"description": "llm描述描述",
|
||||||
|
"toolbarPosition": "right",
|
||||||
|
"config": {
|
||||||
|
"labelType": "llm"
|
||||||
|
},
|
||||||
|
"handles": [
|
||||||
|
{
|
||||||
|
"id": "27",
|
||||||
|
"position": "left",
|
||||||
|
"type": "source",
|
||||||
|
"connectable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "90",
|
||||||
|
"position": "right",
|
||||||
|
"type": "source",
|
||||||
|
"connectable": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"width": 200,
|
||||||
|
"height": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "873",
|
||||||
|
"type": "llm",
|
||||||
|
"initialized": false,
|
||||||
|
"position": {
|
||||||
|
"x": 561.0654659633774,
|
||||||
|
"y": 547.6673703535477
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"label": "llm6",
|
||||||
|
"icon": "",
|
||||||
|
"description": "llm描述描述",
|
||||||
|
"toolbarPosition": "right",
|
||||||
|
"config": {
|
||||||
|
"labelType": "llm"
|
||||||
|
},
|
||||||
|
"handles": [
|
||||||
|
{
|
||||||
|
"id": "31",
|
||||||
|
"position": "left",
|
||||||
|
"type": "source",
|
||||||
|
"connectable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "51",
|
||||||
|
"position": "right",
|
||||||
|
"type": "source",
|
||||||
|
"connectable": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"width": 200,
|
||||||
|
"height": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "244",
|
||||||
|
"type": "llm",
|
||||||
|
"initialized": false,
|
||||||
|
"position": {
|
||||||
|
"x": 543.1540601095024,
|
||||||
|
"y": 653.309197614774
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"label": "llm7",
|
||||||
|
"icon": "",
|
||||||
|
"description": "llm描述描述",
|
||||||
|
"toolbarPosition": "right",
|
||||||
|
"config": {
|
||||||
|
"labelType": "llm"
|
||||||
|
},
|
||||||
|
"handles": [
|
||||||
|
{
|
||||||
|
"id": "33",
|
||||||
|
"position": "left",
|
||||||
|
"type": "source",
|
||||||
|
"connectable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "13",
|
||||||
|
"position": "right",
|
||||||
|
"type": "source",
|
||||||
|
"connectable": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"width": 200,
|
||||||
|
"height": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "308",
|
||||||
|
"type": "llm",
|
||||||
|
"initialized": false,
|
||||||
|
"position": {
|
||||||
|
"x": 588.7086470279171,
|
||||||
|
"y": 318.8609378786052
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"label": "llm4",
|
||||||
|
"icon": "",
|
||||||
|
"description": "llm描述描述",
|
||||||
|
"toolbarPosition": "right",
|
||||||
|
"config": {
|
||||||
|
"labelType": "llm"
|
||||||
|
},
|
||||||
|
"handles": [
|
||||||
|
{
|
||||||
|
"id": "17",
|
||||||
|
"position": "left",
|
||||||
|
"type": "source",
|
||||||
|
"connectable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "73",
|
||||||
|
"position": "right",
|
||||||
|
"type": "source",
|
||||||
|
"connectable": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"width": 200,
|
||||||
|
"height": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"edges": [
|
"edges": [
|
||||||
{
|
{
|
||||||
"id": "6",
|
"id": "188",
|
||||||
"type": "default",
|
"type": "default",
|
||||||
"source": "5",
|
"source": "5",
|
||||||
"target": "6",
|
"target": "188",
|
||||||
"sourceHandle": "7",
|
"sourceHandle": "51",
|
||||||
|
"targetHandle": "11",
|
||||||
|
"data": {},
|
||||||
|
"label": "",
|
||||||
"animated": true,
|
"animated": true,
|
||||||
"targetHandle": "8",
|
"sourceX": 124.52083333333331,
|
||||||
"label": "线标签",
|
"sourceY": 295.3294270833333,
|
||||||
"markerEnd": "none",
|
"targetX": 262.70866773530213,
|
||||||
"markerStart": "none",
|
"targetY": 98.2431395846395
|
||||||
"updatable": true,
|
},
|
||||||
"selectable": true,
|
{
|
||||||
"deletable": true
|
"id": "vueflow__edge-18845-661",
|
||||||
|
"type": "default",
|
||||||
|
"source": "188",
|
||||||
|
"target": "6",
|
||||||
|
"sourceHandle": "45",
|
||||||
|
"targetHandle": "61",
|
||||||
|
"data": {},
|
||||||
|
"label": "",
|
||||||
|
"sourceX": 472.20841119628,
|
||||||
|
"sourceY": 98.2431395846395,
|
||||||
|
"targetX": 1278.4153889062152,
|
||||||
|
"targetY": 256.12454009635803
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "850",
|
||||||
|
"type": "default",
|
||||||
|
"source": "5",
|
||||||
|
"target": "850",
|
||||||
|
"sourceHandle": "51",
|
||||||
|
"targetHandle": "43",
|
||||||
|
"data": {},
|
||||||
|
"label": "",
|
||||||
|
"animated": true,
|
||||||
|
"sourceX": 124.52083333333331,
|
||||||
|
"sourceY": 295.3294270833333,
|
||||||
|
"targetX": 266.62293379655364,
|
||||||
|
"targetY": 280.6389855757115
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "vueflow__edge-85057-661",
|
||||||
|
"type": "default",
|
||||||
|
"source": "850",
|
||||||
|
"target": "6",
|
||||||
|
"sourceHandle": "57",
|
||||||
|
"targetHandle": "61",
|
||||||
|
"data": {},
|
||||||
|
"label": "",
|
||||||
|
"sourceX": 476.1223496202179,
|
||||||
|
"sourceY": 280.6389855757115,
|
||||||
|
"targetX": 1278.4153889062152,
|
||||||
|
"targetY": 256.12454009635803
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "979",
|
||||||
|
"type": "default",
|
||||||
|
"source": "5",
|
||||||
|
"target": "979",
|
||||||
|
"sourceHandle": "51",
|
||||||
|
"targetHandle": "40",
|
||||||
|
"data": {},
|
||||||
|
"label": "",
|
||||||
|
"animated": true,
|
||||||
|
"sourceX": 124.52083333333331,
|
||||||
|
"sourceY": 295.3294270833333,
|
||||||
|
"targetX": 253.92267015536444,
|
||||||
|
"targetY": 386.83332778318527
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "818",
|
||||||
|
"type": "default",
|
||||||
|
"source": "5",
|
||||||
|
"target": "818",
|
||||||
|
"sourceHandle": "51",
|
||||||
|
"targetHandle": "86",
|
||||||
|
"data": {},
|
||||||
|
"label": "",
|
||||||
|
"animated": true,
|
||||||
|
"sourceX": 124.52083333333331,
|
||||||
|
"sourceY": 295.3294270833333,
|
||||||
|
"targetX": 244.3974724244726,
|
||||||
|
"targetY": 494.2742291332315
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "288",
|
||||||
|
"type": "default",
|
||||||
|
"source": "818",
|
||||||
|
"target": "288",
|
||||||
|
"sourceHandle": "11",
|
||||||
|
"targetHandle": "27",
|
||||||
|
"data": {},
|
||||||
|
"label": "",
|
||||||
|
"animated": true,
|
||||||
|
"sourceX": 453.89688824813686,
|
||||||
|
"sourceY": 494.2742291332315,
|
||||||
|
"targetX": 542.5224239769512,
|
||||||
|
"targetY": 458.2374910466816
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "873",
|
||||||
|
"type": "default",
|
||||||
|
"source": "818",
|
||||||
|
"target": "873",
|
||||||
|
"sourceHandle": "11",
|
||||||
|
"targetHandle": "31",
|
||||||
|
"data": {},
|
||||||
|
"label": "",
|
||||||
|
"animated": true,
|
||||||
|
"sourceX": 453.89688824813686,
|
||||||
|
"sourceY": 494.2742291332315,
|
||||||
|
"targetX": 557.8988921284383,
|
||||||
|
"targetY": 574.5006486940537
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "244",
|
||||||
|
"type": "default",
|
||||||
|
"source": "818",
|
||||||
|
"target": "244",
|
||||||
|
"sourceHandle": "11",
|
||||||
|
"targetHandle": "33",
|
||||||
|
"data": {},
|
||||||
|
"label": "",
|
||||||
|
"animated": true,
|
||||||
|
"sourceX": 453.89688824813686,
|
||||||
|
"sourceY": 494.2742291332315,
|
||||||
|
"targetX": 539.9875200651626,
|
||||||
|
"targetY": 680.1424930622838
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "vueflow__edge-28890-661",
|
||||||
|
"type": "default",
|
||||||
|
"source": "288",
|
||||||
|
"target": "6",
|
||||||
|
"sourceHandle": "90",
|
||||||
|
"targetHandle": "61",
|
||||||
|
"data": {},
|
||||||
|
"label": "",
|
||||||
|
"sourceX": 752.0218882481367,
|
||||||
|
"sourceY": 458.2374910466816,
|
||||||
|
"targetX": 1278.4153889062152,
|
||||||
|
"targetY": 256.12454009635803
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "vueflow__edge-87351-661",
|
||||||
|
"type": "default",
|
||||||
|
"source": "873",
|
||||||
|
"target": "6",
|
||||||
|
"sourceHandle": "51",
|
||||||
|
"targetHandle": "61",
|
||||||
|
"data": {},
|
||||||
|
"label": "",
|
||||||
|
"sourceX": 767.3988903613488,
|
||||||
|
"sourceY": 574.5006486940537,
|
||||||
|
"targetX": 1278.4153889062152,
|
||||||
|
"targetY": 256.12454009635803
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "vueflow__edge-24413-661",
|
||||||
|
"type": "default",
|
||||||
|
"source": "244",
|
||||||
|
"target": "6",
|
||||||
|
"sourceHandle": "13",
|
||||||
|
"targetHandle": "61",
|
||||||
|
"data": {},
|
||||||
|
"label": "",
|
||||||
|
"sourceX": 749.4874144445799,
|
||||||
|
"sourceY": 680.1424930622838,
|
||||||
|
"targetX": 1278.4153889062152,
|
||||||
|
"targetY": 256.12454009635803
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "308",
|
||||||
|
"type": "default",
|
||||||
|
"source": "979",
|
||||||
|
"target": "308",
|
||||||
|
"sourceHandle": "46",
|
||||||
|
"targetHandle": "17",
|
||||||
|
"data": {},
|
||||||
|
"label": "",
|
||||||
|
"animated": true,
|
||||||
|
"sourceX": 463.42208597902874,
|
||||||
|
"sourceY": 386.83332778318527,
|
||||||
|
"targetX": 585.5419963013785,
|
||||||
|
"targetY": 345.6941732530918
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "vueflow__edge-30873-661",
|
||||||
|
"type": "default",
|
||||||
|
"source": "308",
|
||||||
|
"target": "6",
|
||||||
|
"sourceHandle": "73",
|
||||||
|
"targetHandle": "61",
|
||||||
|
"data": {},
|
||||||
|
"label": "",
|
||||||
|
"sourceX": 795.0417833691082,
|
||||||
|
"sourceY": 345.6941732530918,
|
||||||
|
"targetX": 1278.4153889062152,
|
||||||
|
"targetY": 256.12454009635803
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"position": [
|
||||||
|
447.2060780237498,
|
||||||
|
377.80084997067763
|
||||||
|
],
|
||||||
|
"zoom": 0.8311688311688327,
|
||||||
|
"viewport": {
|
||||||
|
"x": 447.2060780237498,
|
||||||
|
"y": 377.80084997067763,
|
||||||
|
"zoom": 0.8311688311688327
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -44,6 +44,11 @@ public class RunningContext {
|
|||||||
this.nodeRunningContext.put(nodeId, nodeRunningContext);
|
this.nodeRunningContext.put(nodeId, nodeRunningContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JSONObject getRunningContext(Long nodeId) {
|
||||||
|
return this.nodeRunningContext.get(nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建上下文
|
* 构建上下文
|
||||||
*
|
*
|
||||||
@@ -58,4 +63,6 @@ public class RunningContext {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,18 @@ public class RunningResult {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建结果
|
||||||
|
*
|
||||||
|
* @param nextRunNodeId 下一个运行节点id
|
||||||
|
* @return {@link RunningResult }
|
||||||
|
*/
|
||||||
|
public static RunningResult buildResult(Set<Long> nextRunNodeId) {
|
||||||
|
return RunningResult.builder()
|
||||||
|
.nextRunNodeId(nextRunNodeId)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建结果
|
* 构建结果
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
package com.metis.domain.entity;
|
|
||||||
|
|
||||||
import com.metis.domain.entity.base.Edge;
|
|
||||||
import com.metis.domain.entity.base.Node;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public class GraphDemo {
|
|
||||||
private Map<Long, Node> nodes = new HashMap<>();
|
|
||||||
private Map<Long, List<Long>> adjacencyList = new HashMap<>();
|
|
||||||
|
|
||||||
public void addNode(Node node) {
|
|
||||||
nodes.put(node.getId(), node);
|
|
||||||
adjacencyList.put(node.getId(), new ArrayList<>());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addEdge(Edge edge) {
|
|
||||||
adjacencyList.get(edge.getSource())
|
|
||||||
.add(edge.getTarget());
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Node> topologicalSort() {
|
|
||||||
List<Node> sortedNodes = new ArrayList<>();
|
|
||||||
Set<Long> visited = new HashSet<>();
|
|
||||||
Set<Long> visiting = new HashSet<>();
|
|
||||||
|
|
||||||
for (Long nodeId : nodes.keySet()) {
|
|
||||||
if (!visited.contains(nodeId)) {
|
|
||||||
dfs(nodeId, visited, visiting, sortedNodes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Collections.reverse(sortedNodes);
|
|
||||||
return sortedNodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void dfs(Long nodeId, Set<Long> visited, Set<Long> visiting, List<Node> sortedNodes) {
|
|
||||||
if (visiting.contains(nodeId)) {
|
|
||||||
throw new IllegalStateException("Cycle detected in the graph");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!visited.contains(nodeId)) {
|
|
||||||
visiting.add(nodeId);
|
|
||||||
for (Long neighbor : adjacencyList.get(nodeId)) {
|
|
||||||
dfs(neighbor, visited, visiting, sortedNodes);
|
|
||||||
}
|
|
||||||
visiting.remove(nodeId);
|
|
||||||
visited.add(nodeId);
|
|
||||||
sortedNodes.add(nodes.get(nodeId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
package com.metis.domain.entity;
|
||||||
|
|
||||||
|
|
||||||
|
import com.metis.domain.entity.base.Edge;
|
||||||
|
import com.metis.domain.entity.base.Graph;
|
||||||
|
import com.metis.domain.entity.base.Node;
|
||||||
|
import com.metis.enums.NodeType;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class GraphDto {
|
||||||
|
|
||||||
|
private final Map<Long, Node> nodeMap;
|
||||||
|
|
||||||
|
private final Map<Long, Boolean> nodeReadyMap;
|
||||||
|
|
||||||
|
private final Map<Long, List<Edge>> edgeMap;
|
||||||
|
|
||||||
|
private final Map<Long, List<Long>> adjacencyList = new HashMap<>();
|
||||||
|
|
||||||
|
private final List<Node> sortedNodes = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
public List<Node> getSortedNodes() {
|
||||||
|
return new ArrayList<>(sortedNodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Edge> getEdgeNodeId(Long nodeId) {
|
||||||
|
return edgeMap.getOrDefault(nodeId, new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node getEndNode(){
|
||||||
|
return sortedNodes.stream()
|
||||||
|
.filter(node -> NodeType.END.equals(node.getType()))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node getNode(Long nodeId) {
|
||||||
|
return nodeMap.get(nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateNodeReadyMap(Long nodeId, Boolean ready) {
|
||||||
|
nodeReadyMap.put(nodeId, ready);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean isNodeReady(Long nodeId) {
|
||||||
|
return nodeReadyMap.get(nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private GraphDto(List<Node> nodes, List<Edge> edges) {
|
||||||
|
this.edgeMap = edges.stream()
|
||||||
|
.collect(Collectors.groupingBy(Edge::getSource));
|
||||||
|
this.nodeMap = nodes.stream()
|
||||||
|
.collect(Collectors.toMap(Node::getId, Function.identity()));
|
||||||
|
this.nodeReadyMap = nodes.stream()
|
||||||
|
.collect(Collectors.toMap(Node::getId, node -> false));
|
||||||
|
initAdjacencyList(edges);
|
||||||
|
List<Node> nodeList = topologicalSort();
|
||||||
|
this.sortedNodes.addAll(nodeList);
|
||||||
|
Node node = sortedNodes.get(0);
|
||||||
|
if (NodeType.START.equals(node.getType())) {
|
||||||
|
nodeReadyMap.put(node.getId(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initAdjacencyList(List<Edge> edges) {
|
||||||
|
for (Edge edge : edges) {
|
||||||
|
List<Long> targetList = adjacencyList.getOrDefault(edge.getSource(), new ArrayList<>());
|
||||||
|
targetList.add(edge.getTarget());
|
||||||
|
adjacencyList.put(edge.getSource(), targetList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拓扑排序
|
||||||
|
*
|
||||||
|
* @return {@link List }<{@link Node }>
|
||||||
|
*/
|
||||||
|
private List<Node> topologicalSort() {
|
||||||
|
List<Node> sortedNodes = new ArrayList<>();
|
||||||
|
Set<Long> visited = new HashSet<>();
|
||||||
|
Set<Long> visiting = new HashSet<>();
|
||||||
|
for (Long nodeId : nodeMap.keySet()) {
|
||||||
|
if (!visited.contains(nodeId)) {
|
||||||
|
dfs(nodeId, visited, visiting, sortedNodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Collections.reverse(sortedNodes);
|
||||||
|
return sortedNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 深度遍历找到运行顺序
|
||||||
|
*
|
||||||
|
* @param nodeId 节点id
|
||||||
|
* @param visited 参观了
|
||||||
|
* @param visiting 参观
|
||||||
|
* @param sortedNodes 排序节点
|
||||||
|
*/
|
||||||
|
private void dfs(Long nodeId, Set<Long> visited, Set<Long> visiting, List<Node> sortedNodes) {
|
||||||
|
if (visiting.contains(nodeId)) {
|
||||||
|
throw new IllegalStateException("Cycle detected in the graph");
|
||||||
|
}
|
||||||
|
if (!visited.contains(nodeId)) {
|
||||||
|
visiting.add(nodeId);
|
||||||
|
for (Long neighbor : adjacencyList.getOrDefault(nodeId, new ArrayList<>())) {
|
||||||
|
dfs(neighbor, visited, visiting, sortedNodes);
|
||||||
|
}
|
||||||
|
visiting.remove(nodeId);
|
||||||
|
visited.add(nodeId);
|
||||||
|
sortedNodes.add(nodeMap.get(nodeId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建对象
|
||||||
|
*
|
||||||
|
* @param graph 图
|
||||||
|
* @return {@link GraphDto }
|
||||||
|
*/
|
||||||
|
public static GraphDto of(Graph graph) {
|
||||||
|
return new GraphDto(graph.getNodes(), graph.getEdges());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.metis.domain.entity.config.node;
|
||||||
|
|
||||||
|
import com.metis.domain.entity.base.NodeConfig;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class LLMNodeConfig extends NodeConfig {
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.metis.domain.entity.config.node;
|
||||||
|
|
||||||
|
import com.metis.domain.entity.base.NodeConfig;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class QuestionClassifierConfig extends NodeConfig {
|
||||||
|
}
|
||||||
@@ -68,6 +68,7 @@ public class AppEngineServiceImpl implements AppEngineService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public App create(CreateApp createApp) {
|
public App create(CreateApp createApp) {
|
||||||
BuildApp buildApp = BaseAppConvert.INSTANCE.toBuildApp(createApp);
|
BuildApp buildApp = BaseAppConvert.INSTANCE.toBuildApp(createApp);
|
||||||
// 校验
|
// 校验
|
||||||
|
|||||||
@@ -5,12 +5,14 @@ import cn.hutool.core.lang.Assert;
|
|||||||
import cn.hutool.core.util.IdUtil;
|
import cn.hutool.core.util.IdUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.metis.domain.context.RunningContext;
|
import com.metis.domain.context.RunningContext;
|
||||||
import com.metis.domain.context.RunningResult;
|
import com.metis.domain.context.RunningResult;
|
||||||
import com.metis.domain.context.SysContext;
|
import com.metis.domain.context.SysContext;
|
||||||
import com.metis.domain.entity.App;
|
import com.metis.domain.entity.App;
|
||||||
|
import com.metis.domain.entity.GraphDto;
|
||||||
import com.metis.domain.entity.base.Edge;
|
import com.metis.domain.entity.base.Edge;
|
||||||
import com.metis.domain.entity.base.Graph;
|
|
||||||
import com.metis.domain.entity.base.Node;
|
import com.metis.domain.entity.base.Node;
|
||||||
import com.metis.engine.AppEngineService;
|
import com.metis.engine.AppEngineService;
|
||||||
import com.metis.engine.AppFlowEngineRunnerService;
|
import com.metis.engine.AppFlowEngineRunnerService;
|
||||||
@@ -25,8 +27,6 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@@ -40,7 +40,7 @@ public class AppFlowEngineRunnerServiceImpl implements AppFlowEngineRunnerServic
|
|||||||
public RunnerResult running(FlowRunningContext context) {
|
public RunnerResult running(FlowRunningContext context) {
|
||||||
App app = getApp(context);
|
App app = getApp(context);
|
||||||
Assert.isTrue(ObjectUtil.isNotNull(app), "app为空");
|
Assert.isTrue(ObjectUtil.isNotNull(app), "app为空");
|
||||||
// todo 构建运行实例, 并将运行实例放入上下文
|
// 构建运行实例, 并将运行实例放入上下文
|
||||||
Long instanceId = IdUtil.getSnowflakeNextId();
|
Long instanceId = IdUtil.getSnowflakeNextId();
|
||||||
// 构建系统上下文信息
|
// 构建系统上下文信息
|
||||||
SysContext sysContext = SysContext.builder()
|
SysContext sysContext = SysContext.builder()
|
||||||
@@ -52,26 +52,49 @@ public class AppFlowEngineRunnerServiceImpl implements AppFlowEngineRunnerServic
|
|||||||
// 构建运行中上下文
|
// 构建运行中上下文
|
||||||
RunningContext runningContext = RunningContext.buildContext(sysContext, context);
|
RunningContext runningContext = RunningContext.buildContext(sysContext, context);
|
||||||
// 构建节点映射对象
|
// 构建节点映射对象
|
||||||
Graph graph = app.getGraph();
|
GraphDto graph = GraphDto.of(app.getGraph());
|
||||||
Map<Long, Node> nodeMap = graph.getNodes().stream()
|
|
||||||
.collect(Collectors.toMap(Node::getId, Function.identity()));
|
|
||||||
Map<Long, List<Edge>> edgeMap = graph.getEdges().stream()
|
|
||||||
.collect(Collectors.groupingBy(Edge::getSource));
|
|
||||||
Set<Node> readyRunningNode = new HashSet<>();
|
Set<Node> readyRunningNode = new HashSet<>();
|
||||||
|
|
||||||
// 获取到开始节点
|
// 获取到开始节点
|
||||||
// 开始节点为空,则表示数据存在异常
|
// 开始节点为空,则表示数据存在异常
|
||||||
Assert.isTrue(ObjectUtil.isNotNull(readyRunningNode), "流程图不存在开始节点");
|
Assert.isTrue(ObjectUtil.isNotNull(readyRunningNode), "流程图不存在开始节点");
|
||||||
while (CollUtil.isNotEmpty(readyRunningNode)) {
|
for (Node node : graph.getSortedNodes()) {
|
||||||
// todo 出现多个节点同时运行, 需要找到他们最终运行的聚合节点, 前期默认只有一条线路运行, 不支持并行流程
|
Long nodeId = node.getId();
|
||||||
doRunning(readyRunningNode, edgeMap, runningContext);
|
if (!graph.isNodeReady(nodeId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
readyRunningNode = null;
|
log.info("当前运行节点 id:{}, name:{}, type:{}", node.getId(), node.getData().getLabel(), node.getType());
|
||||||
|
// 当前节点接下来的连接线信息
|
||||||
|
List<Edge> edges = graph.getEdgeNodeId(nodeId);
|
||||||
|
// 执行
|
||||||
|
NodeRunner nodeRunner = getNodeRunner(node);
|
||||||
|
node.setConfigClass(GenericInterfacesUtils.getClass(nodeRunner));
|
||||||
|
// 下一个需要运行的节点id加入到可以运行的节点中
|
||||||
|
// 获取到返回结果
|
||||||
|
RunningResult result = nodeRunner.run(runningContext, node, edges);
|
||||||
|
log.info("节点执行结果:{}", JSON.toJSONString(result));
|
||||||
|
// 节点执行结果参数放入上下文中
|
||||||
|
if (ObjectUtil.isNotNull(result.getNodeContext())) {
|
||||||
|
runningContext.addNodeRunningContext(node.getId(), result.getNodeContext());
|
||||||
|
}
|
||||||
|
// 下一个需要运行的节点id加入到可以运行的节点中
|
||||||
|
if (CollUtil.isNotEmpty(result.getNextRunNodeId())) {
|
||||||
|
for (Long nextNodeId : result.getNextRunNodeId()) {
|
||||||
|
graph.updateNodeReadyMap(nextNodeId, true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果没有返回, 则认为所有的下级节点都需要运行
|
||||||
|
edges.forEach(edge -> {
|
||||||
|
graph.updateNodeReadyMap(edge.getTarget(), true);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Node endNode = graph.getEndNode();
|
||||||
|
|
||||||
|
JSONObject endRunningContext = runningContext.getRunningContext(endNode.getId());
|
||||||
|
|
||||||
return RunnerResult.builder()
|
return RunnerResult.builder()
|
||||||
.content("你他妈的!")
|
.result(endRunningContext)
|
||||||
.context(sysContext)
|
.context(sysContext)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@@ -113,7 +136,7 @@ public class AppFlowEngineRunnerServiceImpl implements AppFlowEngineRunnerServic
|
|||||||
* @return {@link NodeRunner }
|
* @return {@link NodeRunner }
|
||||||
*/
|
*/
|
||||||
private NodeRunner getNodeRunner(Node node) {
|
private NodeRunner getNodeRunner(Node node) {
|
||||||
if (NodeType.CUSTOM_NODE.equals(node.getType())) {
|
if (NodeType.CUSTOM.equals(node.getType())) {
|
||||||
Assert.isTrue(StrUtil.isNotBlank(node.getCustomType()), "自定义节点类型不能为空");
|
Assert.isTrue(StrUtil.isNotBlank(node.getCustomType()), "自定义节点类型不能为空");
|
||||||
return NodeRunnerFactory.getCustom(node.getCustomType());
|
return NodeRunnerFactory.getCustom(node.getCustomType());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,14 @@ public enum NodeType {
|
|||||||
|
|
||||||
START(1, "start", "开始"),
|
START(1, "start", "开始"),
|
||||||
END(2, "end", "结束"),
|
END(2, "end", "结束"),
|
||||||
DOCUMENT_EXTRACTOR(3, "document-extractor", "文档提取器"),
|
DOCUMENT_EXTRACTOR(3, "documentExtractor", "文档提取器"),
|
||||||
CUSTOM_NODE(4, "Custom-Node", "自定义节点");
|
CUSTOM(4, "custom", "自定义节点"),
|
||||||
|
LLM(5, "llm", "LLM"),
|
||||||
|
QUESTION_CLASSIFIER(6, "questionClassifier", "问题分类器"),
|
||||||
|
IF_ELSE(7, "ifElse", "条件判断"),
|
||||||
|
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
private final Integer code;
|
private final Integer code;
|
||||||
@@ -24,8 +30,6 @@ public enum NodeType {
|
|||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
// private final Class<?> configClass;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 枚举序列化器(前端传code时自动转换为对应枚举)
|
* 枚举序列化器(前端传code时自动转换为对应枚举)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package com.metis.facade;
|
package com.metis.facade;
|
||||||
|
|
||||||
import com.metis.domain.bo.ProcessBo;
|
|
||||||
import com.metis.convert.GraphConvert;
|
import com.metis.convert.GraphConvert;
|
||||||
import com.metis.domain.bo.CreateApp;
|
import com.metis.domain.bo.CreateApp;
|
||||||
|
import com.metis.domain.bo.ProcessBo;
|
||||||
import com.metis.domain.bo.UpdateApp;
|
import com.metis.domain.bo.UpdateApp;
|
||||||
import com.metis.domain.entity.App;
|
import com.metis.domain.entity.App;
|
||||||
import com.metis.domain.entity.base.Graph;
|
import com.metis.domain.entity.base.Graph;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public interface CustomNodeRunner<T extends NodeConfig> extends NodeRunner<T> {
|
|||||||
* @return {@link NodeType }
|
* @return {@link NodeType }
|
||||||
*/
|
*/
|
||||||
default NodeType getType() {
|
default NodeType getType() {
|
||||||
return NodeType.CUSTOM_NODE;
|
return NodeType.CUSTOM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.metis.runner;
|
package com.metis.runner;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import com.metis.domain.context.RunningContext;
|
import com.metis.domain.context.RunningContext;
|
||||||
import com.metis.domain.context.RunningResult;
|
import com.metis.domain.context.RunningResult;
|
||||||
import com.metis.domain.entity.base.Edge;
|
import com.metis.domain.entity.base.Edge;
|
||||||
@@ -8,6 +9,8 @@ import com.metis.domain.entity.base.NodeConfig;
|
|||||||
import com.metis.enums.NodeType;
|
import com.metis.enums.NodeType;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 内置节点运行器
|
* 内置节点运行器
|
||||||
@@ -36,4 +39,18 @@ public interface NodeRunner<T extends NodeConfig> {
|
|||||||
*/
|
*/
|
||||||
NodeType getType();
|
NodeType getType();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取下一个节点id
|
||||||
|
*
|
||||||
|
* @param edges 边缘
|
||||||
|
* @return {@link Set }<{@link Long }>
|
||||||
|
*/
|
||||||
|
default Set<Long> getNextNodeIds(List<Edge> edges) {
|
||||||
|
if (CollUtil.isEmpty(edges)) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
return edges.stream().map(Edge::getTarget).collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
package com.metis.runner;
|
package com.metis.runner;
|
||||||
|
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.metis.domain.context.SysContext;
|
import com.metis.domain.context.SysContext;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 运行结果
|
* 运行结果
|
||||||
*
|
*
|
||||||
@@ -18,7 +21,7 @@ public class RunnerResult {
|
|||||||
/**
|
/**
|
||||||
* 运行内容
|
* 运行内容
|
||||||
*/
|
*/
|
||||||
private String content;
|
private JSONObject result;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上下文
|
* 上下文
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public class RunnerInitialize implements ApplicationContextAware {
|
|||||||
Map<String, NodeRunner> runnerMap = applicationContext.getBeansOfType(NodeRunner.class);
|
Map<String, NodeRunner> runnerMap = applicationContext.getBeansOfType(NodeRunner.class);
|
||||||
|
|
||||||
runnerMap.forEach((runnerBeanName, runner) -> {
|
runnerMap.forEach((runnerBeanName, runner) -> {
|
||||||
if (NodeType.CUSTOM_NODE.equals(runner.getType())) {
|
if (NodeType.CUSTOM.equals(runner.getType())) {
|
||||||
Assert.isTrue(runner instanceof CustomNodeRunner, "自定义节点必须实现CustomNodeRunner接口");
|
Assert.isTrue(runner instanceof CustomNodeRunner, "自定义节点必须实现CustomNodeRunner接口");
|
||||||
NodeRunnerFactory.registerCustom((CustomNodeRunner) runner);
|
NodeRunnerFactory.registerCustom((CustomNodeRunner) runner);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.metis.runner.impl;
|
package com.metis.runner.impl;
|
||||||
|
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.metis.domain.context.RunningContext;
|
import com.metis.domain.context.RunningContext;
|
||||||
import com.metis.domain.context.RunningResult;
|
import com.metis.domain.context.RunningResult;
|
||||||
import com.metis.domain.entity.base.Edge;
|
import com.metis.domain.entity.base.Edge;
|
||||||
@@ -19,7 +20,9 @@ public class EndNodeRunner implements NodeRunner<EndNodeConfig> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RunningResult run(RunningContext context, Node node, List<Edge> edges) {
|
public RunningResult run(RunningContext context, Node node, List<Edge> edges) {
|
||||||
return RunningResult.buildResult();
|
JSONObject contextNodeValue = new JSONObject();
|
||||||
|
contextNodeValue.put("userId", context.getSys().getAppId());
|
||||||
|
return RunningResult.buildResult(contextNodeValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.metis.runner.impl;
|
||||||
|
|
||||||
|
import com.metis.domain.context.RunningContext;
|
||||||
|
import com.metis.domain.context.RunningResult;
|
||||||
|
import com.metis.domain.entity.base.Edge;
|
||||||
|
import com.metis.domain.entity.base.Node;
|
||||||
|
import com.metis.domain.entity.config.node.LLMNodeConfig;
|
||||||
|
import com.metis.enums.NodeType;
|
||||||
|
import com.metis.runner.NodeRunner;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class LLMNodeRunner implements NodeRunner<LLMNodeConfig> {
|
||||||
|
@Override
|
||||||
|
public RunningResult run(RunningContext context, Node node, List<Edge> edges) {
|
||||||
|
return RunningResult.buildResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NodeType getType() {
|
||||||
|
return NodeType.LLM;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.metis.runner.impl;
|
||||||
|
|
||||||
|
import com.metis.domain.context.RunningContext;
|
||||||
|
import com.metis.domain.context.RunningResult;
|
||||||
|
import com.metis.domain.entity.base.Edge;
|
||||||
|
import com.metis.domain.entity.base.Node;
|
||||||
|
import com.metis.domain.entity.config.node.QuestionClassifierConfig;
|
||||||
|
import com.metis.enums.NodeType;
|
||||||
|
import com.metis.runner.NodeRunner;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class QuestionClassifierRunner implements NodeRunner<QuestionClassifierConfig> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RunningResult run(RunningContext context, Node node, List<Edge> edges) {
|
||||||
|
Set<Long> nextNodeIds = getNextNodeIds(edges);
|
||||||
|
// 生成随机索引
|
||||||
|
Random random = new Random();
|
||||||
|
int randomIndex = random.nextInt(nextNodeIds.size());
|
||||||
|
List<Long> nodeIds = new ArrayList<>(nextNodeIds);
|
||||||
|
return RunningResult.buildResult(Set.of(nodeIds.get(randomIndex)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NodeType getType() {
|
||||||
|
return NodeType.QUESTION_CLASSIFIER;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,7 +27,6 @@ public class StartNodeRunner implements NodeRunner<StartNodeConfig> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RunningResult run(RunningContext context, Node node, List<Edge> edges) {
|
public RunningResult run(RunningContext context, Node node, List<Edge> edges) {
|
||||||
log.info("开始节点{}, 节点id: {} 运行", node.getData().getLabel(), node.getId());
|
|
||||||
StartNodeConfig config = node.getConfig();
|
StartNodeConfig config = node.getConfig();
|
||||||
// 获取到节点的自定义参数
|
// 获取到节点的自定义参数
|
||||||
List<NodeVariable> variables = config.getVariables();
|
List<NodeVariable> variables = config.getVariables();
|
||||||
@@ -46,7 +45,6 @@ public class StartNodeRunner implements NodeRunner<StartNodeConfig> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NodeType getType() {
|
public NodeType getType() {
|
||||||
return NodeType.START;
|
return NodeType.START;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public interface CustomNodeValidator<T extends NodeConfig> extends NodeValidator
|
|||||||
* @return {@link NodeType }
|
* @return {@link NodeType }
|
||||||
*/
|
*/
|
||||||
default NodeType getType() {
|
default NodeType getType() {
|
||||||
return NodeType.CUSTOM_NODE;
|
return NodeType.CUSTOM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public class ValidatorInitialize implements ApplicationContextAware {
|
|||||||
Map<String, NodeValidator> nodeMap = applicationContext.getBeansOfType(NodeValidator.class);
|
Map<String, NodeValidator> nodeMap = applicationContext.getBeansOfType(NodeValidator.class);
|
||||||
|
|
||||||
nodeMap.forEach((nodeValidatorBeanName, nodeValidator) -> {
|
nodeMap.forEach((nodeValidatorBeanName, nodeValidator) -> {
|
||||||
if (NodeType.CUSTOM_NODE.equals(nodeValidator.getType())) {
|
if (NodeType.CUSTOM.equals(nodeValidator.getType())) {
|
||||||
Assert.isTrue(nodeValidator instanceof CustomNodeValidator, "自定义节点必须实现CustomNodeValidator接口");
|
Assert.isTrue(nodeValidator instanceof CustomNodeValidator, "自定义节点必须实现CustomNodeValidator接口");
|
||||||
NodeValidatorFactory.registerCustom((CustomNodeValidator) nodeValidator);
|
NodeValidatorFactory.registerCustom((CustomNodeValidator) nodeValidator);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -258,7 +258,7 @@ public class ValidatorServiceImpl implements ValidatorService {
|
|||||||
* @return {@link NodeValidator }
|
* @return {@link NodeValidator }
|
||||||
*/
|
*/
|
||||||
private NodeValidator getNodeValidator(Node node) {
|
private NodeValidator getNodeValidator(Node node) {
|
||||||
if (NodeType.CUSTOM_NODE.equals(node.getType())) {
|
if (NodeType.CUSTOM.equals(node.getType())) {
|
||||||
Assert.isTrue(StrUtil.isNotBlank(node.getCustomType()), "自定义节点类型不能为空");
|
Assert.isTrue(StrUtil.isNotBlank(node.getCustomType()), "自定义节点类型不能为空");
|
||||||
return NodeValidatorFactory.getCustom(node.getCustomType());
|
return NodeValidatorFactory.getCustom(node.getCustomType());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ public class EndNodeValidator implements NodeValidator<EndNodeConfig> {
|
|||||||
Assert.isTrue(targets.isEmpty(), "结束节点 {} 不允许有目标连接", node.getId());
|
Assert.isTrue(targets.isEmpty(), "结束节点 {} 不允许有目标连接", node.getId());
|
||||||
|
|
||||||
// 2. 检查 sources 数量是否小于 handles 数量
|
// 2. 检查 sources 数量是否小于 handles 数量
|
||||||
int handleCount = node.getData().getHandles().size();
|
// int handleCount = node.getData().getHandles().size();
|
||||||
Assert.isTrue(sources.size() <= handleCount, "结束节点 {} 的源连接数超过 handles 数量", node.getId());
|
// Assert.isTrue(sources.size() <= handleCount, "结束节点 {} 的源连接数超过 handles 数量", node.getId());
|
||||||
|
|
||||||
return ValidatorResult.valid();
|
return ValidatorResult.valid();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.metis.validator.impl.node;
|
||||||
|
|
||||||
|
import com.metis.domain.entity.base.Edge;
|
||||||
|
import com.metis.domain.entity.base.Node;
|
||||||
|
import com.metis.domain.entity.config.node.LLMNodeConfig;
|
||||||
|
import com.metis.enums.NodeType;
|
||||||
|
import com.metis.validator.NodeValidator;
|
||||||
|
import com.metis.validator.ValidatorResult;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class LLMNodeValidator implements NodeValidator<LLMNodeConfig> {
|
||||||
|
@Override
|
||||||
|
public ValidatorResult validateValue(Node node) {
|
||||||
|
return ValidatorResult.valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValidatorResult validateRelation(Node node, List<Edge> sources, List<Edge> targets) {
|
||||||
|
return ValidatorResult.valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NodeType getType() {
|
||||||
|
return NodeType.LLM;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.metis.validator.impl.node;
|
||||||
|
|
||||||
|
import com.metis.domain.entity.base.Edge;
|
||||||
|
import com.metis.domain.entity.base.Node;
|
||||||
|
import com.metis.domain.entity.config.node.QuestionClassifierConfig;
|
||||||
|
import com.metis.enums.NodeType;
|
||||||
|
import com.metis.validator.NodeValidator;
|
||||||
|
import com.metis.validator.ValidatorResult;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class QuestionClassifierValidator implements NodeValidator<QuestionClassifierConfig> {
|
||||||
|
@Override
|
||||||
|
public ValidatorResult validateValue(Node node) {
|
||||||
|
return ValidatorResult.valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValidatorResult validateRelation(Node node, List<Edge> sources, List<Edge> targets) {
|
||||||
|
return ValidatorResult.valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NodeType getType() {
|
||||||
|
return NodeType.QUESTION_CLASSIFIER;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -84,8 +84,8 @@ public class StartNodeValidator implements NodeValidator<StartNodeConfig> {
|
|||||||
Assert.isTrue(sources.isEmpty(), "开始节点 {} 不允许有源连接", node.getId());
|
Assert.isTrue(sources.isEmpty(), "开始节点 {} 不允许有源连接", node.getId());
|
||||||
|
|
||||||
// 2. 检查 targets 数量是否小于 handles 数量
|
// 2. 检查 targets 数量是否小于 handles 数量
|
||||||
int handleCount = node.getData().getHandles().size();
|
// int handleCount = node.getData().getHandles().size();
|
||||||
Assert.isTrue(targets.size() <= handleCount, "开始节点 {} 的目标连接数超过 handles 数量", node.getId());
|
// Assert.isTrue(targets.size() <= handleCount, "开始节点 {} 的目标连接数超过 handles 数量", node.getId());
|
||||||
|
|
||||||
return ValidatorResult.valid();
|
return ValidatorResult.valid();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user