|
| 1 | +# coding=utf-8 |
| 2 | +""" |
| 3 | + @project: MaxKB |
| 4 | + @Author:虎虎虎 |
| 5 | + @file: workflow.py |
| 6 | + @date:2026/6/29 10:58 |
| 7 | + @desc: |
| 8 | +""" |
| 9 | +from enum import Enum |
| 10 | +from typing import List, Dict |
| 11 | + |
| 12 | +from common.utils.common import group_by |
| 13 | + |
| 14 | + |
| 15 | +class Node: |
| 16 | + |
| 17 | + def __init__(self, _id: str, _type: str, x: int, y: int, properties: dict, **kwargs): |
| 18 | + """ |
| 19 | +
|
| 20 | + @param _id: 节点id |
| 21 | + @param _type: 类型 |
| 22 | + @param x: 节点x轴位置 |
| 23 | + @param y: 节点y轴位置 |
| 24 | + @param properties: |
| 25 | + @param kwargs: |
| 26 | + """ |
| 27 | + self.id = _id |
| 28 | + self.type = _type |
| 29 | + self.x = x |
| 30 | + self.y = y |
| 31 | + self.properties = properties |
| 32 | + for keyword in kwargs: |
| 33 | + self.__setattr__(keyword, kwargs.get(keyword)) |
| 34 | + |
| 35 | + |
| 36 | +class Edge: |
| 37 | + def __init__(self, _id: str, _type: str, sourceNodeId: str, targetNodeId: str, **keywords): |
| 38 | + """ |
| 39 | + 线 |
| 40 | + @param _id: 线id |
| 41 | + @param _type: 线类型 |
| 42 | + @param sourceNodeId: |
| 43 | + @param targetNodeId: |
| 44 | + @param keywords: |
| 45 | + """ |
| 46 | + self.id = _id |
| 47 | + self.type = _type |
| 48 | + self.sourceNodeId = sourceNodeId |
| 49 | + self.targetNodeId = targetNodeId |
| 50 | + for keyword in keywords: |
| 51 | + self.__setattr__(keyword, keywords.get(keyword)) |
| 52 | + |
| 53 | + |
| 54 | +class EdgeNode: |
| 55 | + edge: Edge |
| 56 | + node: Node |
| 57 | + |
| 58 | + def __init__(self, edge, node): |
| 59 | + self.edge = edge |
| 60 | + self.node = node |
| 61 | + |
| 62 | + |
| 63 | +def init_fields(workflow): |
| 64 | + result = [] |
| 65 | + for node in workflow.nodes: |
| 66 | + properties = node.properties |
| 67 | + node_name = properties.get('stepName') |
| 68 | + node_id = node.id |
| 69 | + node_config = properties.get('config') |
| 70 | + result.append(NodeField(node_id, node_name, '异常信息', 'exception_message')) |
| 71 | + if node_config is not None: |
| 72 | + fields = node_config.get('fields') |
| 73 | + if fields is not None: |
| 74 | + for field in fields: |
| 75 | + result.append(NodeField(node_id, node_name, field.get('label'), field.get('value'))) |
| 76 | + global_fields = node_config.get('globalFields') |
| 77 | + if global_fields is not None: |
| 78 | + for global_field in global_fields: |
| 79 | + result.append(NodeField('global', '全局变量', global_field.get('label'), global_field.get('value'))) |
| 80 | + chat_fields = node_config.get('chatFields') |
| 81 | + if chat_fields is not None: |
| 82 | + for chat_field in chat_fields: |
| 83 | + result.append(NodeField('chat', 'chat', chat_field.get('label'), chat_field.get('value'))) |
| 84 | + result.sort(key=lambda f: len(f.node_name + f.value), reverse=True) |
| 85 | + return result |
| 86 | + |
| 87 | + |
| 88 | +def get_node_parameters(node): |
| 89 | + return node.properties.get('node_data', {}) |
| 90 | + |
| 91 | + |
| 92 | +class NodeField: |
| 93 | + def __init__(self, node_id, node_name, label, value): |
| 94 | + self.node_id = node_id |
| 95 | + self.node_name = node_name |
| 96 | + self.label = label |
| 97 | + self.value = value |
| 98 | + |
| 99 | + def reset_variable(self, prompt: str): |
| 100 | + userVariable = self.node_name + "." + self.value |
| 101 | + systemVariable = f"context.get('{self.node_id}').get('{self.value}','')" |
| 102 | + return prompt.replace(userVariable, systemVariable) |
| 103 | + |
| 104 | + |
| 105 | +class Workflow: |
| 106 | + """ |
| 107 | + 节点列表 |
| 108 | + """ |
| 109 | + nodes: List[Node] |
| 110 | + """ |
| 111 | + 线列表 |
| 112 | + """ |
| 113 | + edges: List[Edge] |
| 114 | + """ |
| 115 | + 节点id:node |
| 116 | + """ |
| 117 | + node_map: Dict[str, Node] |
| 118 | + """ |
| 119 | + 节点id:当前节点id上面的所有节点 |
| 120 | + """ |
| 121 | + up_node_map: Dict[str, List[EdgeNode]] |
| 122 | + """ |
| 123 | + 节点id:当前节点id下面的所有节点 |
| 124 | + """ |
| 125 | + next_node_map: Dict[str, List[EdgeNode]] |
| 126 | + """ |
| 127 | + 节点字段 |
| 128 | + """ |
| 129 | + node_field_list: List[NodeField] |
| 130 | + |
| 131 | + def __init__(self, nodes: List[Node], edges: List[Edge]): |
| 132 | + self.nodes = nodes |
| 133 | + self.edges = edges |
| 134 | + self.node_map = {node.id: node for node in nodes} |
| 135 | + |
| 136 | + self.up_node_map = {key: [EdgeNode(edge, self.node_map.get(edge.sourceNodeId)) for |
| 137 | + edge in edges] for |
| 138 | + key, edges in |
| 139 | + group_by(edges, key=lambda edge: edge.targetNodeId).items()} |
| 140 | + |
| 141 | + self.next_node_map = {key: [EdgeNode(edge, self.node_map.get(edge.targetNodeId)) for edge in edges] for |
| 142 | + key, edges in |
| 143 | + group_by(edges, key=lambda edge: edge.sourceNodeId).items()} |
| 144 | + self.node_field_list = init_fields(self) |
| 145 | + |
| 146 | + def get_node(self, node_id): |
| 147 | + """ |
| 148 | + 根据node_id 获取节点信息 |
| 149 | + @param node_id: node_id |
| 150 | + @return: 节点信息 |
| 151 | + """ |
| 152 | + return self.node_map.get(node_id) |
| 153 | + |
| 154 | + def get_up_edge_nodes(self, node_id) -> List[EdgeNode]: |
| 155 | + """ |
| 156 | + 根据节点id 获取当前连接前置节点和连线 |
| 157 | + @param node_id: 节点id |
| 158 | + @return: 节点连线列表 |
| 159 | + """ |
| 160 | + return self.up_node_map.get(node_id) |
| 161 | + |
| 162 | + def get_next_edge_nodes(self, node_id) -> List[EdgeNode]: |
| 163 | + """ |
| 164 | + 根据节点id 获取当前连接目标节点和连线 |
| 165 | + @param node_id: 节点id |
| 166 | + @return: 节点连线列表 |
| 167 | + """ |
| 168 | + return self.next_node_map.get(node_id) |
| 169 | + |
| 170 | + def get_up_nodes(self, node_id) -> List[Node]: |
| 171 | + """ |
| 172 | + 根据节点id 获取当前连接前置节点 |
| 173 | + @param node_id: 节点id |
| 174 | + @return: 节点列表 |
| 175 | + """ |
| 176 | + return [en.node for en in self.up_node_map.get(node_id)] |
| 177 | + |
| 178 | + def get_next_nodes(self, node_id) -> List[Node]: |
| 179 | + """ |
| 180 | + 根据节点id 获取当前连接目标节点 |
| 181 | + @param node_id: 节点id |
| 182 | + @return: 节点列表 |
| 183 | + """ |
| 184 | + return [en.node for en in self.next_node_map.get(node_id, [])] |
| 185 | + |
| 186 | + def reset_prompt(self, prompt): |
| 187 | + for node_field in self.node_field_list: |
| 188 | + prompt = node_field.reset_variable(prompt) |
| 189 | + return prompt |
| 190 | + |
| 191 | + |
| 192 | +class WorkflowType(Enum): |
| 193 | + # 应用 |
| 194 | + APPLICATION = "APPLICATION" |
| 195 | + # 知识库 |
| 196 | + KNOWLEDGE = "KNOWLEDGE" |
| 197 | + # 工具 |
| 198 | + TOOL = "TOOL" |
| 199 | + |
| 200 | + |
| 201 | +def new_instance(flow_obj: Dict, workflow_type: WorkflowType = WorkflowType.APPLICATION): |
| 202 | + nodes = flow_obj.get('nodes') |
| 203 | + edges = flow_obj.get('edges') |
| 204 | + nodes = [Node(node.get('id'), node.get('type'), **node) for node in nodes] |
| 205 | + edges = [Edge(edge.get('id'), edge.get('type'), **edge) for edge in edges] |
| 206 | + return Workflow(nodes, edges) |
0 commit comments