Skip to content

Commit bc1a480

Browse files
committed
feat: knowledge workflow
1 parent 7eddb4b commit bc1a480

File tree

48 files changed

+275
-80
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+275
-80
lines changed

apps/application/flow/common.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
@date:2024/12/11 17:57
77
@desc:
88
"""
9-
9+
from enum import Enum
1010
from typing import List, Dict
1111

1212
from django.db.models import QuerySet
@@ -90,6 +90,16 @@ def __init__(self, edge, node):
9090
self.node = node
9191

9292

93+
class WorkflowMode(Enum):
94+
APPLICATION = "application"
95+
96+
APPLICATION_LOOP = "application-loop"
97+
98+
KNOWLEDGE = "knowledge"
99+
100+
KNOWLEDGE_LOOP = "knowledge-loop"
101+
102+
93103
class Workflow:
94104
"""
95105
节点列表
@@ -112,7 +122,10 @@ class Workflow:
112122
"""
113123
next_node_map: Dict[str, List[EdgeNode]]
114124

115-
def __init__(self, nodes: List[Node], edges: List[Edge]):
125+
workflow_mode: WorkflowMode
126+
127+
def __init__(self, nodes: List[Node], edges: List[Edge],
128+
workflow_mode: WorkflowMode = WorkflowMode.APPLICATION.value):
116129
self.nodes = nodes
117130
self.edges = edges
118131
self.node_map = {node.id: node for node in nodes}
@@ -125,6 +138,7 @@ def __init__(self, nodes: List[Node], edges: List[Edge]):
125138
self.next_node_map = {key: [EdgeNode(edge, self.node_map.get(edge.targetNodeId)) for edge in edges] for
126139
key, edges in
127140
group_by(edges, key=lambda edge: edge.sourceNodeId).items()}
141+
self.workflow_mode = workflow_mode
128142

129143
def get_node(self, node_id):
130144
"""
@@ -167,13 +181,13 @@ def get_next_nodes(self, node_id) -> List[Node]:
167181
return [en.node for en in self.next_node_map.get(node_id, [])]
168182

169183
@staticmethod
170-
def new_instance(flow_obj: Dict):
184+
def new_instance(flow_obj: Dict, workflow_mode: WorkflowMode = WorkflowMode.APPLICATION.value):
171185
nodes = flow_obj.get('nodes')
172186
edges = flow_obj.get('edges')
173187
nodes = [Node(node.get('id'), node.get('type'), **node)
174188
for node in nodes]
175189
edges = [Edge(edge.get('id'), edge.get('type'), **edge) for edge in edges]
176-
return Workflow(nodes, edges)
190+
return Workflow(nodes, edges, workflow_mode)
177191

178192
def get_start_node(self):
179193
return self.get_node('start-node')
@@ -190,10 +204,9 @@ def is_valid(self):
190204
self.is_valid_base_node()
191205
self.is_valid_work_flow()
192206

193-
@staticmethod
194-
def is_valid_node_params(node: Node):
207+
def is_valid_node_params(self, node: Node):
195208
from application.flow.step_node import get_node
196-
get_node(node.type)(node, None, None)
209+
get_node(node.type, self.workflow_mode)(node, None, None)
197210

198211
def is_valid_node(self, node: Node):
199212
self.is_valid_node_params(node)

apps/application/flow/i_step_node.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,14 @@ def handler(self, workflow):
9696
application_public_access_client.save()
9797

9898

99+
class KnowledgeWorkflowPostHandler(WorkFlowPostHandler):
100+
def __init__(self, chat_info):
101+
super().__init__(chat_info)
102+
103+
def handler(self, workflow):
104+
pass
105+
106+
99107
class NodeResult:
100108
def __init__(self, node_variable: Dict, workflow_variable: Dict,
101109
_write_context=write_context, _is_interrupt=is_interrupt):
@@ -152,6 +160,12 @@ class FlowParamsSerializer(serializers.Serializer):
152160
debug = serializers.BooleanField(required=True, label="是否debug")
153161

154162

163+
class KnowledgeFlowParamsSerializer(serializers.Serializer):
164+
knowledge_id = serializers.CharField(required=True, label="知识库id")
165+
data_source = serializers.DictField(required=True, label="数据源")
166+
knowledge_base = serializers.DictField(required=False, label="知识库设置")
167+
168+
155169
class INode:
156170
view_type = 'many_view'
157171

@@ -221,7 +235,7 @@ def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
221235
pass
222236

223237
def get_flow_params_serializer_class(self) -> Type[serializers.Serializer]:
224-
return FlowParamsSerializer
238+
return self.workflow_manage.get_params_serializer_class()
225239

226240
def get_write_error_context(self, e):
227241
self.status = 500
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# coding=utf-8
2+
"""
3+
@project: MaxKB
4+
@Author:虎虎
5+
@file: Knowledge_workflow_manage.py
6+
@date:2025/11/13 19:02
7+
@desc:
8+
"""
9+
10+
from application.flow.common import Workflow
11+
from application.flow.i_step_node import WorkFlowPostHandler, KnowledgeFlowParamsSerializer
12+
from application.flow.workflow_manage import WorkflowManage
13+
from common.handle.base_to_response import BaseToResponse
14+
from common.handle.impl.response.system_to_response import SystemToResponse
15+
16+
17+
class KnowledgeWorkflowManage(WorkflowManage):
18+
19+
def __init__(self, flow: Workflow,
20+
params,
21+
work_flow_post_handler: WorkFlowPostHandler,
22+
base_to_response: BaseToResponse = SystemToResponse(),
23+
start_node_id=None,
24+
start_node_data=None, chat_record=None, child_node=None):
25+
super().__init__(flow, params, work_flow_post_handler, base_to_response, None, None, None,
26+
None,
27+
None, None, start_node_id, start_node_data, chat_record, child_node)
28+
29+
def get_params_serializer_class(self):
30+
return KnowledgeFlowParamsSerializer
31+
32+
def get_start_node(self):
33+
start_node_list = [node for node in self.flow.nodes if
34+
self.params.get('data_source', {}).get('node_id') == node.id]
35+
return start_node_list[0]

apps/application/flow/loop_workflow_manage.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,10 @@ def get_node_cls_by_id(self, node_id, up_node_id_list=None,
105105
get_node_params=lambda node: node.properties.get('node_data')):
106106
for node in self.flow.nodes:
107107
if node.id == node_id:
108-
node_instance = get_node(node.type)(node,
109-
self.params, self, up_node_id_list,
110-
get_node_params,
111-
salt=self.get_index())
108+
node_instance = get_node(node.type, self.flow.workflow_mode)(node,
109+
self.params, self, up_node_id_list,
110+
get_node_params,
111+
salt=self.get_index())
112112
return node_instance
113113
return None
114114

apps/application/flow/step_node/__init__.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,14 @@
5050
BaseIntentNode, BaseLoopNode, BaseLoopStartStepNode,
5151
BaseLoopContinueNode,
5252
BaseLoopBreakNode, BaseVariableSplittingNode, BaseParameterExtractionNode, BaseVariableAggregationNode,
53+
<<<<<<< Updated upstream
5354
BaseDataSourceLocalNode,BaseDataSourceWebNode,BaseKnowledgeWriteNode]
55+
=======
56+
BaseDataSourceLocalNode, BaseDataSourceWebNode]
5457

58+
node_map = {n.type: {w: n for w in n.support} for n in node_list}
59+
>>>>>>> Stashed changes
5560

56-
def get_node(node_type):
57-
find_list = [node for node in node_list if node.type == node_type]
58-
if len(find_list) > 0:
59-
return find_list[0]
60-
return None
61+
62+
def get_node(node_type, workflow_model):
63+
return node_map.get(node_type).get(workflow_model)

apps/application/flow/step_node/ai_chat_step_node/i_chat_node.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from django.utils.translation import gettext_lazy as _
1212
from rest_framework import serializers
1313

14+
from application.flow.common import WorkflowMode
1415
from application.flow.i_step_node import INode, NodeResult
1516

1617

@@ -34,22 +35,31 @@ class ChatNodeSerializer(serializers.Serializer):
3435
mcp_enable = serializers.BooleanField(required=False, label=_("Whether to enable MCP"))
3536
mcp_servers = serializers.JSONField(required=False, label=_("MCP Server"))
3637
mcp_tool_id = serializers.CharField(required=False, allow_blank=True, allow_null=True, label=_("MCP Tool ID"))
37-
mcp_tool_ids = serializers.ListField(child=serializers.UUIDField(), required=False, allow_empty=True, label=_("MCP Tool IDs"), )
38+
mcp_tool_ids = serializers.ListField(child=serializers.UUIDField(), required=False, allow_empty=True,
39+
label=_("MCP Tool IDs"), )
3840
mcp_source = serializers.CharField(required=False, allow_blank=True, allow_null=True, label=_("MCP Source"))
3941

4042
tool_enable = serializers.BooleanField(required=False, default=False, label=_("Whether to enable tools"))
4143
tool_ids = serializers.ListField(child=serializers.UUIDField(), required=False, allow_empty=True,
4244
label=_("Tool IDs"), )
4345
mcp_output_enable = serializers.BooleanField(required=False, default=True, label=_("Whether to enable MCP output"))
4446

47+
4548
class IChatNode(INode):
4649
type = 'ai-chat-node'
50+
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP, WorkflowMode.KNOWLEDGE_LOOP,
51+
WorkflowMode.KNOWLEDGE]
4752

4853
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
4954
return ChatNodeSerializer
5055

5156
def _run(self):
52-
return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data)
57+
if [WorkflowMode.KNOWLEDGE, WorkflowMode.APPLICATION_LOOP].__contains__(
58+
self.workflow_manage.flow.workflow_mode):
59+
return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data,
60+
**{'history_chat_record': [], 'stream': True, 'chat_id': None, 'chat_record_id': None})
61+
else:
62+
return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data)
5363

5464
def execute(self, model_id, system, prompt, dialogue_number, history_chat_record, stream, chat_id,
5565
chat_record_id,

apps/application/flow/step_node/application_node/i_application_node.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from rest_framework import serializers
55

6+
from application.flow.common import WorkflowMode
67
from application.flow.i_step_node import INode, NodeResult
78

89
from django.utils.translation import gettext_lazy as _
@@ -25,6 +26,7 @@ class ApplicationNodeSerializer(serializers.Serializer):
2526

2627
class IApplicationNode(INode):
2728
type = 'application-node'
29+
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP]
2830

2931
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
3032
return ApplicationNodeSerializer

apps/application/flow/step_node/condition_node/i_condition_node.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from django.utils.translation import gettext_lazy as _
1212
from rest_framework import serializers
1313

14+
from application.flow.common import WorkflowMode
1415
from application.flow.i_step_node import INode
1516

1617

@@ -36,3 +37,5 @@ def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
3637
return ConditionNodeParamsSerializer
3738

3839
type = 'condition-node'
40+
41+
support = [WorkflowMode.APPLICATION_LOOP]

apps/application/flow/step_node/data_source_local_node/i_data_source_local_node.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from django.utils.translation import gettext_lazy as _
1313
from rest_framework import serializers
1414

15+
from application.flow.common import WorkflowMode
1516
from application.flow.i_step_node import INode, NodeResult
1617

1718

@@ -35,5 +36,7 @@ def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
3536
def _run(self):
3637
return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data)
3738

38-
def execute(self, file_format, max_file_number, file_max_size, **kwargs) -> NodeResult:
39+
def execute(self, file_type_list, file_size_limit, file_count_limit, **kwargs) -> NodeResult:
3940
pass
41+
42+
support = [WorkflowMode.KNOWLEDGE]

apps/application/flow/step_node/data_source_local_node/impl/base_data_source_local_node.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,6 @@ def get_form_list(node):
3434
'label': '',
3535
}]
3636

37-
def execute(self, file_format, max_file_number, file_max_size, **kwargs) -> NodeResult:
38-
pass
37+
def execute(self, file_type_list, file_size_limit, file_count_limit, **kwargs) -> NodeResult:
38+
return NodeResult({'file_list': self.workflow_manage.params.get('data_source', {}).get('file_list')},
39+
self.workflow_manage.params.get('knowledge_base') or {})

0 commit comments

Comments
 (0)