feat: knowledge workflow

This commit is contained in:
shaohuzhang1 2025-11-17 19:08:43 +08:00
parent 7eddb4bd83
commit bc1a4809e0
48 changed files with 275 additions and 80 deletions

View File

@ -6,7 +6,7 @@
@date2024/12/11 17:57
@desc:
"""
from enum import Enum
from typing import List, Dict
from django.db.models import QuerySet
@ -90,6 +90,16 @@ class EdgeNode:
self.node = node
class WorkflowMode(Enum):
APPLICATION = "application"
APPLICATION_LOOP = "application-loop"
KNOWLEDGE = "knowledge"
KNOWLEDGE_LOOP = "knowledge-loop"
class Workflow:
"""
节点列表
@ -112,7 +122,10 @@ class Workflow:
"""
next_node_map: Dict[str, List[EdgeNode]]
def __init__(self, nodes: List[Node], edges: List[Edge]):
workflow_mode: WorkflowMode
def __init__(self, nodes: List[Node], edges: List[Edge],
workflow_mode: WorkflowMode = WorkflowMode.APPLICATION.value):
self.nodes = nodes
self.edges = edges
self.node_map = {node.id: node for node in nodes}
@ -125,6 +138,7 @@ class Workflow:
self.next_node_map = {key: [EdgeNode(edge, self.node_map.get(edge.targetNodeId)) for edge in edges] for
key, edges in
group_by(edges, key=lambda edge: edge.sourceNodeId).items()}
self.workflow_mode = workflow_mode
def get_node(self, node_id):
"""
@ -167,13 +181,13 @@ class Workflow:
return [en.node for en in self.next_node_map.get(node_id, [])]
@staticmethod
def new_instance(flow_obj: Dict):
def new_instance(flow_obj: Dict, workflow_mode: WorkflowMode = WorkflowMode.APPLICATION.value):
nodes = flow_obj.get('nodes')
edges = flow_obj.get('edges')
nodes = [Node(node.get('id'), node.get('type'), **node)
for node in nodes]
edges = [Edge(edge.get('id'), edge.get('type'), **edge) for edge in edges]
return Workflow(nodes, edges)
return Workflow(nodes, edges, workflow_mode)
def get_start_node(self):
return self.get_node('start-node')
@ -190,10 +204,9 @@ class Workflow:
self.is_valid_base_node()
self.is_valid_work_flow()
@staticmethod
def is_valid_node_params(node: Node):
def is_valid_node_params(self, node: Node):
from application.flow.step_node import get_node
get_node(node.type)(node, None, None)
get_node(node.type, self.workflow_mode)(node, None, None)
def is_valid_node(self, node: Node):
self.is_valid_node_params(node)

View File

@ -96,6 +96,14 @@ class WorkFlowPostHandler:
application_public_access_client.save()
class KnowledgeWorkflowPostHandler(WorkFlowPostHandler):
def __init__(self, chat_info):
super().__init__(chat_info)
def handler(self, workflow):
pass
class NodeResult:
def __init__(self, node_variable: Dict, workflow_variable: Dict,
_write_context=write_context, _is_interrupt=is_interrupt):
@ -152,6 +160,12 @@ class FlowParamsSerializer(serializers.Serializer):
debug = serializers.BooleanField(required=True, label="是否debug")
class KnowledgeFlowParamsSerializer(serializers.Serializer):
knowledge_id = serializers.CharField(required=True, label="知识库id")
data_source = serializers.DictField(required=True, label="数据源")
knowledge_base = serializers.DictField(required=False, label="知识库设置")
class INode:
view_type = 'many_view'
@ -221,7 +235,7 @@ class INode:
pass
def get_flow_params_serializer_class(self) -> Type[serializers.Serializer]:
return FlowParamsSerializer
return self.workflow_manage.get_params_serializer_class()
def get_write_error_context(self, e):
self.status = 500

View File

@ -0,0 +1,35 @@
# coding=utf-8
"""
@project: MaxKB
@Author虎虎
@file Knowledge_workflow_manage.py
@date2025/11/13 19:02
@desc:
"""
from application.flow.common import Workflow
from application.flow.i_step_node import WorkFlowPostHandler, KnowledgeFlowParamsSerializer
from application.flow.workflow_manage import WorkflowManage
from common.handle.base_to_response import BaseToResponse
from common.handle.impl.response.system_to_response import SystemToResponse
class KnowledgeWorkflowManage(WorkflowManage):
def __init__(self, flow: Workflow,
params,
work_flow_post_handler: WorkFlowPostHandler,
base_to_response: BaseToResponse = SystemToResponse(),
start_node_id=None,
start_node_data=None, chat_record=None, child_node=None):
super().__init__(flow, params, work_flow_post_handler, base_to_response, None, None, None,
None,
None, None, start_node_id, start_node_data, chat_record, child_node)
def get_params_serializer_class(self):
return KnowledgeFlowParamsSerializer
def get_start_node(self):
start_node_list = [node for node in self.flow.nodes if
self.params.get('data_source', {}).get('node_id') == node.id]
return start_node_list[0]

View File

@ -105,10 +105,10 @@ class LoopWorkflowManage(WorkflowManage):
get_node_params=lambda node: node.properties.get('node_data')):
for node in self.flow.nodes:
if node.id == node_id:
node_instance = get_node(node.type)(node,
self.params, self, up_node_id_list,
get_node_params,
salt=self.get_index())
node_instance = get_node(node.type, self.flow.workflow_mode)(node,
self.params, self, up_node_id_list,
get_node_params,
salt=self.get_index())
return node_instance
return None

View File

@ -50,11 +50,14 @@ node_list = [BaseStartStepNode, BaseChatNode, BaseSearchKnowledgeNode, BaseSearc
BaseIntentNode, BaseLoopNode, BaseLoopStartStepNode,
BaseLoopContinueNode,
BaseLoopBreakNode, BaseVariableSplittingNode, BaseParameterExtractionNode, BaseVariableAggregationNode,
<<<<<<< Updated upstream
BaseDataSourceLocalNode,BaseDataSourceWebNode,BaseKnowledgeWriteNode]
=======
BaseDataSourceLocalNode, BaseDataSourceWebNode]
node_map = {n.type: {w: n for w in n.support} for n in node_list}
>>>>>>> Stashed changes
def get_node(node_type):
find_list = [node for node in node_list if node.type == node_type]
if len(find_list) > 0:
return find_list[0]
return None
def get_node(node_type, workflow_model):
return node_map.get(node_type).get(workflow_model)

View File

@ -11,6 +11,7 @@ from typing import Type
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
@ -34,7 +35,8 @@ class ChatNodeSerializer(serializers.Serializer):
mcp_enable = serializers.BooleanField(required=False, label=_("Whether to enable MCP"))
mcp_servers = serializers.JSONField(required=False, label=_("MCP Server"))
mcp_tool_id = serializers.CharField(required=False, allow_blank=True, allow_null=True, label=_("MCP Tool ID"))
mcp_tool_ids = serializers.ListField(child=serializers.UUIDField(), required=False, allow_empty=True, label=_("MCP Tool IDs"), )
mcp_tool_ids = serializers.ListField(child=serializers.UUIDField(), required=False, allow_empty=True,
label=_("MCP Tool IDs"), )
mcp_source = serializers.CharField(required=False, allow_blank=True, allow_null=True, label=_("MCP Source"))
tool_enable = serializers.BooleanField(required=False, default=False, label=_("Whether to enable tools"))
@ -42,14 +44,22 @@ class ChatNodeSerializer(serializers.Serializer):
label=_("Tool IDs"), )
mcp_output_enable = serializers.BooleanField(required=False, default=True, label=_("Whether to enable MCP output"))
class IChatNode(INode):
type = 'ai-chat-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP, WorkflowMode.KNOWLEDGE_LOOP,
WorkflowMode.KNOWLEDGE]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return ChatNodeSerializer
def _run(self):
return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data)
if [WorkflowMode.KNOWLEDGE, WorkflowMode.APPLICATION_LOOP].__contains__(
self.workflow_manage.flow.workflow_mode):
return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data,
**{'history_chat_record': [], 'stream': True, 'chat_id': None, 'chat_record_id': None})
else:
return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data)
def execute(self, model_id, system, prompt, dialogue_number, history_chat_record, stream, chat_id,
chat_record_id,

View File

@ -3,6 +3,7 @@ from typing import Type
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
from django.utils.translation import gettext_lazy as _
@ -25,6 +26,7 @@ class ApplicationNodeSerializer(serializers.Serializer):
class IApplicationNode(INode):
type = 'application-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return ApplicationNodeSerializer

View File

@ -11,6 +11,7 @@ from typing import Type
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode
@ -36,3 +37,5 @@ class IConditionNode(INode):
return ConditionNodeParamsSerializer
type = 'condition-node'
support = [WorkflowMode.APPLICATION_LOOP]

View File

@ -12,6 +12,7 @@ from typing import Type
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
@ -35,5 +36,7 @@ class IDataSourceLocalNode(INode):
def _run(self):
return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data)
def execute(self, file_format, max_file_number, file_max_size, **kwargs) -> NodeResult:
def execute(self, file_type_list, file_size_limit, file_count_limit, **kwargs) -> NodeResult:
pass
support = [WorkflowMode.KNOWLEDGE]

View File

@ -34,5 +34,6 @@ class BaseDataSourceLocalNode(IDataSourceLocalNode):
'label': '',
}]
def execute(self, file_format, max_file_number, file_max_size, **kwargs) -> NodeResult:
pass
def execute(self, file_type_list, file_size_limit, file_count_limit, **kwargs) -> NodeResult:
return NodeResult({'file_list': self.workflow_manage.params.get('data_source', {}).get('file_list')},
self.workflow_manage.params.get('knowledge_base') or {})

View File

@ -8,13 +8,13 @@
"""
from abc import abstractmethod
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
class IDataSourceWebNode(INode):
type = 'data-source-web-node'
support = [WorkflowMode.KNOWLEDGE]
@staticmethod
@abstractmethod
@ -24,7 +24,5 @@ class IDataSourceWebNode(INode):
def _run(self):
return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data)
def execute(self, **kwargs) -> NodeResult:
pass

View File

@ -10,6 +10,7 @@ from typing import Type
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
from common.exception.app_exception import AppApiException
@ -38,6 +39,8 @@ class ReplyNodeParamsSerializer(serializers.Serializer):
class IReplyNode(INode):
type = 'reply-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP, WorkflowMode.KNOWLEDGE_LOOP,
WorkflowMode.KNOWLEDGE]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return ReplyNodeParamsSerializer

View File

@ -5,6 +5,7 @@ from typing import Type
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
@ -14,7 +15,8 @@ class DocumentExtractNodeSerializer(serializers.Serializer):
class IDocumentExtractNode(INode):
type = 'document-extract-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP, WorkflowMode.KNOWLEDGE_LOOP,
WorkflowMode.KNOWLEDGE]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return DocumentExtractNodeSerializer

View File

@ -10,6 +10,7 @@ from typing import Type
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
from django.utils.translation import gettext_lazy as _
@ -24,6 +25,7 @@ class FormNodeParamsSerializer(serializers.Serializer):
class IFormNode(INode):
type = 'form-node'
view_type = 'single_view'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return FormNodeParamsSerializer

View File

@ -5,6 +5,7 @@ from typing import Type
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
@ -31,6 +32,7 @@ class ImageGenerateNodeSerializer(serializers.Serializer):
class IImageGenerateNode(INode):
type = 'image-generate-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return ImageGenerateNodeSerializer

View File

@ -5,6 +5,7 @@ from typing import Type
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
@ -34,6 +35,7 @@ class ImageToVideoNodeSerializer(serializers.Serializer):
class IImageToVideoNode(INode):
type = 'image-to-video-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return ImageToVideoNodeSerializer

View File

@ -4,6 +4,7 @@ from typing import Type
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
from django.utils.translation import gettext_lazy as _
@ -30,6 +31,7 @@ class ImageUnderstandNodeSerializer(serializers.Serializer):
class IImageUnderstandNode(INode):
type = 'image-understand-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return ImageUnderstandNodeSerializer

View File

@ -5,11 +5,11 @@ from typing import Type
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
class IntentBranchSerializer(serializers.Serializer):
id = serializers.CharField(required=True, label=_("Branch id"))
content = serializers.CharField(required=True, label=_("content"))
isOther = serializers.BooleanField(required=True, label=_("Branch Type"))
@ -24,8 +24,11 @@ class IntentNodeSerializer(serializers.Serializer):
label=_("Model parameter settings"))
branch = IntentBranchSerializer(many=True)
class IIntentNode(INode):
type = 'intent-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP]
def save_context(self, details, workflow_manage):
pass
@ -38,9 +41,9 @@ class IIntentNode(INode):
self.node_params_serializer.data.get('content_list')[1:],
)
return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data, user_input=str(question))
return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data,
user_input=str(question))
def execute(self, model_id, dialogue_number, history_chat_record, user_input, branch,
model_params_setting=None, **kwargs) -> NodeResult:
pass
pass

View File

@ -11,6 +11,7 @@ from typing import Type
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode
from application.flow.i_step_node import NodeResult
@ -28,6 +29,7 @@ class LoopBreakNodeSerializer(serializers.Serializer):
class ILoopBreakNode(INode):
type = 'loop-break-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return LoopBreakNodeSerializer

View File

@ -8,10 +8,11 @@
"""
from typing import Type
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
from django.utils.translation import gettext_lazy as _
class ConditionSerializer(serializers.Serializer):
@ -27,6 +28,7 @@ class LoopContinueNodeSerializer(serializers.Serializer):
class ILoopContinueNode(INode):
type = 'loop-continue-node'
support = [WorkflowMode.APPLICATION_LOOP]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return LoopContinueNodeSerializer

View File

@ -11,6 +11,7 @@ from typing import Type
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
from common.exception.app_exception import AppApiException
@ -40,6 +41,7 @@ class ILoopNodeSerializer(serializers.Serializer):
class ILoopNode(INode):
type = 'loop-node'
support = [WorkflowMode.APPLICATION]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return ILoopNodeSerializer

View File

@ -6,15 +6,16 @@
@date2024/6/3 16:54
@desc:
"""
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
class ILoopStarNode(INode):
type = 'loop-start-node'
support = [WorkflowMode.APPLICATION_LOOP]
def _run(self):
return self.execute(**self.flow_params_serializer.data)
def execute(self, **kwargs) -> NodeResult:
def execute(self, **kwargs) -> NodeResult:
pass

View File

@ -5,6 +5,7 @@ from typing import Type
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
@ -19,6 +20,7 @@ class McpNodeSerializer(serializers.Serializer):
class IMcpNode(INode):
type = 'mcp-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return McpNodeSerializer

View File

@ -5,6 +5,7 @@ from typing import Type
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
@ -23,6 +24,8 @@ class VariableSplittingNodeParamsSerializer(serializers.Serializer):
class IParameterExtractionNode(INode):
type = 'parameter-extraction-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP, WorkflowMode.KNOWLEDGE,
WorkflowMode.KNOWLEDGE_LOOP]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return VariableSplittingNodeParamsSerializer

View File

@ -11,6 +11,7 @@ from typing import Type
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
@ -31,6 +32,7 @@ class QuestionNodeSerializer(serializers.Serializer):
class IQuestionNode(INode):
type = 'question-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return QuestionNodeSerializer

View File

@ -10,6 +10,7 @@ from typing import Type
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
from django.utils.translation import gettext_lazy as _
@ -41,6 +42,7 @@ class RerankerStepNodeSerializer(serializers.Serializer):
class IRerankerNode(INode):
type = 'reranker-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return RerankerStepNodeSerializer
@ -57,6 +59,6 @@ class IRerankerNode(INode):
reranker_list=reranker_list)
def execute(self, question, reranker_setting, reranker_list, reranker_model_id,show_knowledge,
def execute(self, question, reranker_setting, reranker_list, reranker_model_id, show_knowledge,
**kwargs) -> NodeResult:
pass

View File

@ -4,6 +4,7 @@ from typing import Type, List
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
@ -42,6 +43,7 @@ class SearchDocumentStepNodeSerializer(serializers.Serializer):
class ISearchDocumentStepNode(INode):
type = 'search-document-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return SearchDocumentStepNodeSerializer

View File

@ -13,6 +13,7 @@ from django.core import validators
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
from common.utils.common import flat_map
@ -67,6 +68,7 @@ def get_paragraph_list(chat_record, node_id):
class ISearchKnowledgeStepNode(INode):
type = 'search-knowledge-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return SearchDatasetStepNodeSerializer

View File

@ -2,10 +2,11 @@
from typing import Type
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
from django.utils.translation import gettext_lazy as _
class SpeechToTextNodeSerializer(serializers.Serializer):
@ -22,6 +23,7 @@ class SpeechToTextNodeSerializer(serializers.Serializer):
class ISpeechToTextNode(INode):
type = 'speech-to-text-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return SpeechToTextNodeSerializer

View File

@ -6,12 +6,13 @@
@date2024/6/3 16:54
@desc:
"""
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
class IStarNode(INode):
type = 'start-node'
support = [WorkflowMode.APPLICATION]
def _run(self):
return self.execute(**self.flow_params_serializer.data)

View File

@ -4,6 +4,7 @@ from typing import Type
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
from django.utils.translation import gettext_lazy as _
@ -22,6 +23,7 @@ class TextToSpeechNodeSerializer(serializers.Serializer):
class ITextToSpeechNode(INode):
type = 'text-to-speech-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return TextToSpeechNodeSerializer

View File

@ -5,6 +5,7 @@ from typing import Type
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
@ -31,6 +32,7 @@ class TextToVideoNodeSerializer(serializers.Serializer):
class ITextToVideoNode(INode):
type = 'text-to-video-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return TextToVideoNodeSerializer

View File

@ -13,6 +13,7 @@ from django.db.models import QuerySet
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
from common.field.common import ObjectField
from tools.models.tool import Tool
@ -40,6 +41,7 @@ class FunctionLibNodeParamsSerializer(serializers.Serializer):
class IToolLibNode(INode):
type = 'tool-lib-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return FunctionLibNodeParamsSerializer

View File

@ -10,15 +10,15 @@ import re
from typing import Type
from django.core import validators
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from rest_framework.utils.formatting import lazy_format
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
from common.exception.app_exception import AppApiException
from common.field.common import ObjectField
from django.utils.translation import gettext_lazy as _
from rest_framework.utils.formatting import lazy_format
class InputField(serializers.Serializer):
name = serializers.CharField(required=True, label=_('Variable Name'))
@ -53,6 +53,7 @@ class FunctionNodeParamsSerializer(serializers.Serializer):
class IToolNode(INode):
type = 'tool-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return FunctionNodeParamsSerializer

View File

@ -5,11 +5,10 @@ from typing import Type
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
class VariableListSerializer(serializers.Serializer):
v_id = serializers.CharField(required=True, label=_("Variable id"))
variable = serializers.ListField(required=True, label=_("Variable"))
@ -29,15 +28,13 @@ class VariableAggregationNodeSerializer(serializers.Serializer):
class IVariableAggregation(INode):
type = 'variable-aggregation-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return VariableAggregationNodeSerializer
def _run(self):
return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data)
return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data)
def execute(self,strategy,group_list,**kwargs) -> NodeResult:
def execute(self, strategy, group_list, **kwargs) -> NodeResult:
pass

View File

@ -5,6 +5,7 @@ from typing import Type
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
@ -15,6 +16,7 @@ class VariableAssignNodeParamsSerializer(serializers.Serializer):
class IVariableAssignNode(INode):
type = 'variable-assign-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return VariableAssignNodeParamsSerializer

View File

@ -5,6 +5,7 @@ from typing import Type
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
@ -18,6 +19,7 @@ class VariableSplittingNodeParamsSerializer(serializers.Serializer):
class IVariableSplittingNode(INode):
type = 'variable-splitting-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return VariableSplittingNodeParamsSerializer

View File

@ -2,12 +2,12 @@
from typing import Type
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult
from django.utils.translation import gettext_lazy as _
class VideoUnderstandNodeSerializer(serializers.Serializer):
model_id = serializers.CharField(required=True, label=_("Model id"))
@ -30,6 +30,7 @@ class VideoUnderstandNodeSerializer(serializers.Serializer):
class IVideoUnderstandNode(INode):
type = 'video-understand-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP]
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return VideoUnderstandNodeSerializer

View File

@ -22,7 +22,7 @@ from rest_framework import status
from application.flow import tools
from application.flow.common import Workflow
from application.flow.i_step_node import INode, WorkFlowPostHandler, NodeResult
from application.flow.i_step_node import INode, WorkFlowPostHandler, NodeResult, FlowParamsSerializer
from application.flow.step_node import get_node
from common.handle.base_to_response import BaseToResponse
from common.handle.impl.response.system_to_response import SystemToResponse
@ -302,8 +302,8 @@ class WorkflowManage:
answer_tokens = sum([row.get('answer_tokens') for row in details.values() if
'answer_tokens' in row and row.get('answer_tokens') is not None])
self.work_flow_post_handler.handler(self)
yield self.base_to_response.to_stream_chunk_response(self.params['chat_id'],
self.params['chat_record_id'],
yield self.base_to_response.to_stream_chunk_response(self.params.get('chat_id'),
self.params.get('chat_record_id'),
'',
[],
'', True, message_tokens, answer_tokens, {})
@ -316,7 +316,7 @@ class WorkflowManage:
translation.activate(language)
if current_node is None:
start_node = self.get_start_node()
current_node = get_node(start_node.type)(start_node, self.params, self)
current_node = get_node(start_node.type, self.flow.workflow_mode)(start_node, self.params, self)
self.node_chunk_manage.add_node_chunk(current_node.node_chunk)
# 添加节点
self.append_node(current_node)
@ -402,8 +402,8 @@ class WorkflowManage:
node_type = r.get("node_type")
view_type = r.get('view_type')
reasoning_content = r.get('reasoning_content')
chunk = self.base_to_response.to_stream_chunk_response(self.params['chat_id'],
self.params['chat_record_id'],
chunk = self.base_to_response.to_stream_chunk_response(self.params.get('chat_id'),
self.params.get('chat_record_id'),
current_node.id,
current_node.up_node_id_list,
content, False, 0, 0,
@ -417,8 +417,8 @@ class WorkflowManage:
'node_status': "SUCCESS"})
current_node.node_chunk.add_chunk(chunk)
chunk = (self.base_to_response
.to_stream_chunk_response(self.params['chat_id'],
self.params['chat_record_id'],
.to_stream_chunk_response(self.params.get('chat_id'),
self.params.get('chat_record_id'),
current_node.id,
current_node.up_node_id_list,
'', False, 0, 0, {'node_is_end': True,
@ -436,8 +436,8 @@ class WorkflowManage:
except Exception as e:
# 添加节点
traceback.print_exc()
chunk = self.base_to_response.to_stream_chunk_response(self.params['chat_id'],
self.params['chat_record_id'],
chunk = self.base_to_response.to_stream_chunk_response(self.params.get('chat_id'),
self.params.get('chat_record_id'),
current_node.id,
current_node.up_node_id_list,
'Exception:' + str(e), False, 0, 0,
@ -698,8 +698,9 @@ class WorkflowManage:
get_node_params=lambda node: node.properties.get('node_data')):
for node in self.flow.nodes:
if node.id == node_id:
node_instance = get_node(node.type)(node,
self.params, self, up_node_id_list, get_node_params)
node_instance = get_node(node.type, self.flow.workflow_mode)(node,
self.params, self, up_node_id_list,
get_node_params)
return node_instance
return None
@ -712,3 +713,6 @@ class WorkflowManage:
def get_node_reference(self, reference_address: Dict):
node = self.get_node_by_id(reference_address.get('node_id'))
return node.context[reference_address.get('node_field')]
def get_params_serializer_class(self):
return FlowParamsSerializer

View File

@ -8,6 +8,9 @@ from django.db.models import QuerySet
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.flow.common import Workflow, WorkflowMode
from application.flow.i_step_node import KnowledgeWorkflowPostHandler
from application.flow.knowledge_workflow_manage import KnowledgeWorkflowManage
from application.flow.step_node import get_node
from common.exception.app_exception import AppApiException
from knowledge.models import KnowledgeScope, Knowledge, KnowledgeType, KnowledgeWorkflow
@ -23,6 +26,23 @@ class KnowledgeWorkflowModelSerializer(serializers.ModelSerializer):
fields = '__all__'
class KnowledgeWorkflowActionSerializer(serializers.Serializer):
workspace_id = serializers.CharField(required=True, label=_('workspace id'))
knowledge_id = serializers.UUIDField(required=True, label=_('knowledge id'))
def action(self, instance: Dict, with_valid=True):
if with_valid:
self.is_valid(raise_exception=True)
knowledge_workflow = QuerySet(KnowledgeWorkflow).filter(knowledge_id=self.data.get("knowledge_id")).first()
work_flow_manage = KnowledgeWorkflowManage(
Workflow.new_instance(knowledge_workflow.work_flow, WorkflowMode.KNOWLEDGE),
{'knowledge_id': self.data.get("knowledge_id"), 'stream': True,
**instance},
KnowledgeWorkflowPostHandler(None))
r = work_flow_manage.run()
return r
class KnowledgeWorkflowSerializer(serializers.Serializer):
class Form(serializers.Serializer):
type = serializers.CharField(required=True, label=_('type'))
@ -32,7 +52,7 @@ class KnowledgeWorkflowSerializer(serializers.Serializer):
def get_form_list(self):
self.is_valid(raise_exception=True)
if self.data.get('type') == 'local':
node = get_node(self.data.get('id'))
node = get_node(self.data.get('id'), WorkflowMode.KNOWLEDGE)
return node.get_form_list(self.data.get("node"))
elif self.data.get('type') == 'tool':
tool = QuerySet(Tool).filter(id=self.data.get("id")).first()

View File

@ -70,5 +70,6 @@ urlpatterns = [
path('workspace/<str:workspace_id>/knowledge/<str:knowledge_id>/document/<int:current_page>/<int:page_size>', views.DocumentView.Page.as_view()),
path('workspace/<str:workspace_id>/knowledge/<int:current_page>/<int:page_size>', views.KnowledgeView.Page.as_view()),
path('workspace/<str:workspace_id>/knowledge/<str:knowledge_id>/form_list/<str:type>/<str:id>', views.KnowledgeWorkflowFormView.as_view()),
path('workspace/<str:workspace_id>/knowledge/<str:knowledge_id>/action', views.KnowledgeWorkflowActionView.as_view())
]

View File

@ -12,7 +12,7 @@ from common.log.log import log
from common.result import result
from knowledge.api.knowledge_workflow import KnowledgeWorkflowApi
from knowledge.serializers.common import get_knowledge_operation_object
from knowledge.serializers.knowledge_workflow import KnowledgeWorkflowSerializer
from knowledge.serializers.knowledge_workflow import KnowledgeWorkflowSerializer, KnowledgeWorkflowActionSerializer
class KnowledgeWorkflowFormView(APIView):
@ -23,6 +23,14 @@ class KnowledgeWorkflowFormView(APIView):
data={'type': type, 'id': id, 'node': request.data.get('node')}).get_form_list())
class KnowledgeWorkflowActionView(APIView):
authentication_classes = [TokenAuth]
def post(self, request: Request, workspace_id: str, knowledge_id: str):
return KnowledgeWorkflowActionSerializer(
data={'workspace_id': workspace_id, 'knowledge_id': knowledge_id}).action(request.data, True)
class KnowledgeWorkflowView(APIView):
authentication_classes = [TokenAuth]

View File

@ -1,7 +1,7 @@
import { Result } from '@/request/Result'
import { get, post, del, put, exportFile, exportExcel } from '@/request/index'
import { type Ref } from 'vue'
import type { pageRequest } from '@/api/type/common'
import type { Dict, pageRequest } from '@/api/type/common'
import type { knowledgeData } from '@/api/type/knowledge'
import useStore from '@/stores'
@ -330,6 +330,13 @@ const getKnowledgeWorkflowFormList: (
) => {
return post(`${prefix.value}/${knowledge_id}/form_list/${type}/${id}`, { node }, {}, loading)
}
const workflowAction: (
knowledge_id: string,
instance: Dict<any>,
loading?: Ref<boolean>,
) => Promise<Result<any>> = (knowledge_id: string, instance, loading) => {
return post(`${prefix.value}/${knowledge_id}/action`, instance, {}, loading)
}
export default {
getKnowledgeList,
@ -356,4 +363,5 @@ export default {
delMulTag,
createWorkflowKnowledge,
getKnowledgeWorkflowFormList,
workflowAction,
}

View File

@ -1,5 +1,5 @@
<template>
<div>
<div v-loading="loading">
<div class="update-info flex p-8-12 border-r-6 mb-16 w-full">
<div class="mt-4">
<AppIcon iconName="app-warning-colorful" style="font-size: 16px"></AppIcon>
@ -69,12 +69,13 @@
</div>
</template>
<script setup lang="ts">
import { computed, useAttrs, nextTick } from 'vue'
import { computed, useAttrs, nextTick, inject, ref } from 'vue'
import type { FormField } from '@/components/dynamics-form/type'
import { MsgError } from '@/utils/message'
import type { UploadFiles } from 'element-plus'
import { filesize, getImgUrl, isRightType } from '@/utils/common'
import { filesize, getImgUrl } from '@/utils/common'
import { t } from '@/locales'
const upload = inject('upload') as any
const attrs = useAttrs() as any
const props = withDefaults(defineProps<{ modelValue?: any; formField: FormField }>(), {
modelValue: () => [],
@ -87,6 +88,8 @@ const onExceed = () => {
)
}
const emit = defineEmits(['update:modelValue'])
const fileArray = ref<any>([])
const loading = ref<boolean>(false)
// on-change
const fileHandleChange = (file: any, fileList: UploadFiles) => {
//1100M
@ -102,8 +105,12 @@ const fileHandleChange = (file: any, fileList: UploadFiles) => {
fileList.splice(-1, 1)
return false
}
emit('update:modelValue', fileList)
upload(file.raw, loading).then((ok: any) => {
const split_path = ok.data.split('/')
const file_id = split_path[split_path.length - 1]
fileArray.value?.push({ name: file.name, file_id, size: file.size })
emit('update:modelValue', fileArray.value)
})
}
function deleteFile(index: number) {
emit(

View File

@ -8,7 +8,7 @@
:before-close="close"
>
<div style="width: 100%; height: 100%">
<ActionVue :workflow="_workflow"></ActionVue>
<ActionVue :workflow="_workflow" :knowledge_id="_knowledge_id"></ActionVue>
</div>
</el-drawer>
</template>
@ -17,12 +17,14 @@ import ActionVue from '@/views/knowledge-workflow/component/action/index.vue'
import { ref } from 'vue'
const drawer = ref<boolean>(false)
const _workflow = ref<any>(null)
const _knowledge_id = ref<string>()
const close = () => {
drawer.value = false
}
const open = (workflow: any) => {
const open = (workflow: any, knowledge_id: string) => {
drawer.value = true
_workflow.value = workflow
_knowledge_id.value = knowledge_id
}
defineExpose({ close, open })
</script>

View File

@ -9,8 +9,8 @@
require-asterisk-position="right"
>
<template #default>
<el-form-item prop="data_source" :rules="base_form_data_rule.data_source">
<el-radio-group @change="sourceChange" v-model="base_form_data.data_source">
<el-form-item prop="node_id" :rules="base_form_data_rule.node_id">
<el-radio-group @change="sourceChange" v-model="base_form_data.node_id">
<el-radio :value="node.id" border size="large" v-for="node in source_node_list">
<div style="display: flex; align-items: center">
<component
@ -52,7 +52,7 @@ const props = defineProps<{
}>()
const loading = ref<boolean>(false)
const dynamicsFormRef = ref<InstanceType<typeof DynamicsForm>>()
const base_form_data = ref<{ data_source: string }>({ data_source: '' })
const base_form_data = ref<{ node_id: string }>({ node_id: '' })
const dynamics_form_data = ref<Dict<any>>({})
const form_data = computed({
get: () => {
@ -82,7 +82,7 @@ const sourceChange = (node_id: string) => {
})
}
const base_form_data_rule = ref<FormRules>({
data_source: {
node_id: {
required: true,
trigger: 'blur',
message: '数据源必选',

View File

@ -11,26 +11,35 @@
<el-button v-if="base_form_list.length > 0 && active == 'data_source'" @click="next"
>下一步</el-button
>
<el-button v-if="base_form_list.length > 0 ? active == 'knowledge_base' : true" type="primary"
<el-button
v-if="base_form_list.length > 0 ? active == 'knowledge_base' : true"
@click="upload"
type="primary"
>Upload
</el-button>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { computed, ref, provide, type Ref } from 'vue'
import DataSource from '@/views/knowledge-workflow/component/action/DataSource.vue'
import applicationApi from '@/api/application/application'
import KnowledgeBase from '@/views/knowledge-workflow/component/action/KnowledgeBase.vue'
import { WorkflowType } from '@/enums/application'
import KnowledgeApi from '@/api/knowledge/knowledge'
provide('upload', (file: any, loading?: Ref<boolean>) => {
return applicationApi.postUploadFile(file, props.knowledge_id, 'KNOWLEDGE', loading)
})
const ak = {
data_source: DataSource,
knowledge_base: KnowledgeBase,
}
const ActionRef = ref()
const form_data = ref<any>()
const form_data = ref<any>({})
const active = ref<'data_source' | 'knowledge_base'>('data_source')
const props = defineProps<{
workflow: any
knowledge_id: string
}>()
const base_form_list = computed(() => {
const kBase = props.workflow?.nodes?.find((n: any) => n.type === WorkflowType.KnowledgeBase)
@ -41,6 +50,7 @@ const base_form_list = computed(() => {
})
const next = () => {
ActionRef.value.validate().then(() => {
form_data.value[active.value] = ActionRef.value.get_data()
active.value = 'knowledge_base'
})
}
@ -49,5 +59,11 @@ const up = () => {
active.value = 'data_source'
})
}
const upload = () => {
ActionRef.value.validate().then(() => {
form_data.value[active.value] = ActionRef.value.get_data()
KnowledgeApi.workflowAction(props.knowledge_id, form_data.value)
})
}
</script>
<style lang="scss" scoped></style>

View File

@ -393,8 +393,7 @@ const clickShowDebug = () => {
...workflow.get_base_node()?.properties.node_data,
work_flow: getGraphData(),
}
console.log('sss', DebugRef.value)
DebugRef.value?.open(graphData)
DebugRef.value?.open(graphData, id)
} catch (e: any) {
console.log(e)
MsgError(e.toString())