Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/application/flow/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ class WorkflowMode(Enum):

KNOWLEDGE_LOOP = "knowledge-loop"

TOOL = "tool"

TOOL_LOOP = "tool-loop"


class Workflow:
"""
Expand Down
40 changes: 40 additions & 0 deletions apps/application/flow/i_step_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from application.models import ChatRecord, ChatUserType
from common.field.common import InstanceField
from knowledge.models.knowledge_action import KnowledgeAction, State
from tools.models import ToolRecord

chat_cache = cache

Expand Down Expand Up @@ -115,6 +116,40 @@ def handler(self, workflow):
'start_time') is not None else 0)


def get_tool_workflow_state(workflow):
if workflow.is_the_task_interrupted():
return State.REVOKED
details = workflow.get_runtime_details()
node_list = details.values()
all_node = [*node_list, *get_loop_workflow_node(node_list)]
err = any([True for value in all_node if value.get('status') == 500 and not value.get('enableException')])
if err:
return State.FAILURE
return State.SUCCESS


class ToolWorkflowPostHandler(WorkFlowPostHandler):
def __init__(self, chat_info, tool_id):
super().__init__(chat_info)
self.tool_id = tool_id

def handler(self, workflow):
state = get_tool_workflow_state(workflow)
record = ToolRecord(id=self.chat_info.tool_record_id, tool_id=self.tool_id,
workspace_id=self.chat_info.workspace_id,
source_type=self.chat_info.source_type,
source_id=self.chat_info.source_id,
state=state,
meta={
'output': workflow.out_context,
'details': workflow.get_runtime_details(),
'answer_text_list': workflow.get_answer_text_list()
})
self.chat_info.set_record(record)
self.chat_info = None
self.tool_id = None


def get_loop_workflow_node(node_list):
result = []
for item in node_list:
Expand Down Expand Up @@ -204,6 +239,11 @@ class KnowledgeFlowParamsSerializer(serializers.Serializer):
knowledge_base = serializers.DictField(required=False, label="知识库设置")


class ToolFlowParamsSerializer(serializers.Serializer):
tool_id = serializers.UUIDField(required=True, label="工具id")
workspace_id = serializers.CharField(required=True, label="工作空间id")


class INode:
view_type = 'many_view'

Expand Down
6 changes: 6 additions & 0 deletions apps/application/flow/knowledge_loop_workflow_manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@
class KnowledgeLoopWorkflowManage(LoopWorkflowManage):
def get_params_serializer_class(self):
return KnowledgeFlowParamsSerializer

def get_source_type(self):
return "KNOWLEDGE"

def get_source_id(self):
return self.params.get('knowledge_id')
6 changes: 6 additions & 0 deletions apps/application/flow/knowledge_workflow_manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,9 @@ def hand_node_result(self, current_node, node_result_future):
current_node.node_chunk.end()
QuerySet(KnowledgeAction).filter(id=self.params.get('knowledge_action_id')).update(
details=self.get_runtime_details())

def get_source_type(self):
return "KNOWLEDGE"

def get_source_id(self):
return self.params.get('knowledge_id')
6 changes: 6 additions & 0 deletions apps/application/flow/loop_workflow_manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,9 @@ def generate_prompt(self, prompt: str):
prompt_template = PromptTemplate.from_template(prompt, template_format='jinja2')
value = prompt_template.format(context=context)
return value

def get_source_type(self):
return "APPLICATION"

def get_source_id(self):
return self.params.get('application_id')
5 changes: 4 additions & 1 deletion apps/application/flow/step_node/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,13 @@
from .text_to_video_step_node.impl.base_text_to_video_node import BaseTextToVideoNode
from .tool_lib_node import *
from .tool_node import *
from .tool_workflow_lib_node import BaseToolWorkflowLibNodeNode
from .variable_aggregation_node.impl.base_variable_aggregation_node import BaseVariableAggregationNode
from .variable_assign_node import BaseVariableAssignNode
from .variable_splitting_node import BaseVariableSplittingNode
from .video_understand_step_node import BaseVideoUnderstandNode
from .document_split_node import BaseDocumentSplitNode
from .tool_start_node import BaseToolStartStepNode

node_list = [BaseStartStepNode, BaseChatNode, BaseSearchKnowledgeNode, BaseSearchDocumentNode, BaseQuestionNode,
BaseConditionNode, BaseReplyNode,
Expand All @@ -51,7 +53,8 @@
BaseIntentNode, BaseLoopNode, BaseLoopStartStepNode,
BaseLoopContinueNode,
BaseLoopBreakNode, BaseVariableSplittingNode, BaseParameterExtractionNode, BaseVariableAggregationNode,
BaseDataSourceLocalNode, BaseDataSourceWebNode, BaseKnowledgeWriteNode, BaseDocumentSplitNode]
BaseDataSourceLocalNode, BaseDataSourceWebNode, BaseKnowledgeWriteNode, BaseDocumentSplitNode,
BaseToolStartStepNode, BaseToolWorkflowLibNodeNode]

node_map = {n.type: {w: n for w in n.support} for n in node_list}

Expand Down
18 changes: 12 additions & 6 deletions apps/application/flow/step_node/ai_chat_step_node/i_chat_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@


class ChatNodeSerializer(serializers.Serializer):
model_id = serializers.CharField(required=True, label=_("Model id"))
model_id = serializers.CharField(required=False, allow_blank=True, allow_null=True, label=_("Model id"))
model_id_type = serializers.CharField(required=False, default='custom', label=_("Model id type"))
model_id_reference = serializers.ListField(required=False, child=serializers.CharField(), allow_empty=True,
label=_("Reference Field"))
system = serializers.CharField(required=False, allow_blank=True, allow_null=True,
label=_("Role Setting"))
prompt = serializers.CharField(required=True, label=_("Prompt word"))
Expand All @@ -41,23 +44,24 @@ class ChatNodeSerializer(serializers.Serializer):
tool_ids = serializers.ListField(child=serializers.UUIDField(), required=False, allow_empty=True,
label=_("Tool IDs"), )
application_ids = serializers.ListField(child=serializers.UUIDField(), required=False, allow_empty=True,
label=_("App IDs"), )
label=_("App IDs"), )
skill_tool_ids = serializers.ListField(child=serializers.UUIDField(), required=False, allow_empty=True,
label=_("Skill IDs"), )
label=_("Skill 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]
WorkflowMode.KNOWLEDGE, WorkflowMode.TOOL, WorkflowMode.TOOL_LOOP]

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

def _run(self):
if [WorkflowMode.KNOWLEDGE, WorkflowMode.KNOWLEDGE_LOOP].__contains__(
self.workflow_manage.flow.workflow_mode):
if [WorkflowMode.KNOWLEDGE, WorkflowMode.KNOWLEDGE_LOOP, WorkflowMode.TOOL,
WorkflowMode.TOOL_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:
Expand All @@ -66,6 +70,8 @@ def _run(self):
def execute(self, model_id, system, prompt, dialogue_number, history_chat_record, stream, chat_id,
chat_record_id,
model_params_setting=None,
model_id_type=None,
model_id_reference=None,
dialogue_type=None,
model_setting=None,
mcp_servers=None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ def save_context(self, details, workflow_manage):

def execute(self, model_id, system, prompt, dialogue_number, history_chat_record, stream, chat_id, chat_record_id,
model_params_setting=None,
model_id_type=None,
model_id_reference=None,
dialogue_type=None,
model_setting=None,
mcp_servers=None,
Expand All @@ -165,8 +167,20 @@ def execute(self, model_id, system, prompt, dialogue_number, history_chat_record
if dialogue_type is None:
dialogue_type = 'WORKFLOW'

if model_params_setting is None:
if model_id_type == 'reference' and model_id_reference:

reference_data = self.workflow_manage.get_reference_field(
model_id_reference[0],
model_id_reference[1:],
)

if reference_data and isinstance(reference_data, dict):
model_id = reference_data.get('model_id', model_id)
model_params_setting = reference_data.get('model_params_setting')

if model_params_setting is None and model_id:
model_params_setting = get_default_model_params_setting(model_id)

if model_setting is None:
model_setting = {'reasoning_content_enable': False, 'reasoning_content_end': '</think>',
'reasoning_content_start': '<think>'}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,5 @@ def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:

type = 'condition-node'

support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP, WorkflowMode.KNOWLEDGE, WorkflowMode.KNOWLEDGE_LOOP]
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP, WorkflowMode.KNOWLEDGE,
WorkflowMode.KNOWLEDGE_LOOP, WorkflowMode.TOOL, WorkflowMode.TOOL_LOOP]
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,14 @@ def is_valid(self, *, raise_exception=False):
class IReplyNode(INode):
type = 'reply-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP, WorkflowMode.KNOWLEDGE_LOOP,
WorkflowMode.KNOWLEDGE]
WorkflowMode.KNOWLEDGE, WorkflowMode.TOOL]

def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return ReplyNodeParamsSerializer

def _run(self):
if [WorkflowMode.KNOWLEDGE, WorkflowMode.KNOWLEDGE_LOOP].__contains__(
if [WorkflowMode.KNOWLEDGE, WorkflowMode.KNOWLEDGE_LOOP, WorkflowMode.TOOL,
WorkflowMode.TOOL_LOOP].__contains__(
self.workflow_manage.flow.workflow_mode):
return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data,
**{'stream': True})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ class DocumentExtractNodeSerializer(serializers.Serializer):
class IDocumentExtractNode(INode):
type = 'document-extract-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP, WorkflowMode.KNOWLEDGE_LOOP,
WorkflowMode.KNOWLEDGE]
WorkflowMode.KNOWLEDGE, WorkflowMode.TOOL, WorkflowMode.TOOL_LOOP]

def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return DocumentExtractNodeSerializer

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ class DocumentSplitNodeSerializer(serializers.Serializer):
class IDocumentSplitNode(INode):
type = 'document-split-node'
support = [
WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP, WorkflowMode.KNOWLEDGE_LOOP, WorkflowMode.KNOWLEDGE
WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP, WorkflowMode.KNOWLEDGE_LOOP, WorkflowMode.KNOWLEDGE,
WorkflowMode.TOOL, WorkflowMode.TOOL_LOOP
]

def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
Expand Down
2 changes: 1 addition & 1 deletion apps/application/flow/step_node/form_node/i_form_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class FormNodeParamsSerializer(serializers.Serializer):
class IFormNode(INode):
type = 'form-node'
view_type = 'single_view'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP]
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP, WorkflowMode.TOOL, WorkflowMode.TOOL_LOOP]

def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return FormNodeParamsSerializer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,9 @@ def get_answer_list(self) -> List[Answer] | None:
value = prompt_template.format(form=form, context=context, runtime_node_id=self.runtime_node_id,
chat_record_id=self.flow_params_serializer.data.get("chat_record_id"),
form_field_list=form_field_list)
return [Answer(value, self.view_type, self.runtime_node_id, self.workflow_params['chat_record_id'], None,
self.runtime_node_id, '')]
return [
Answer(value, self.view_type, self.runtime_node_id, self.workflow_params.get('chat_record_id') or '', None,
self.runtime_node_id, '')]

def get_details(self, index: int, **kwargs):
form_content_format = self.context.get('form_content_format')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,15 @@ class ImageGenerateNodeSerializer(serializers.Serializer):
class IImageGenerateNode(INode):
type = 'image-generate-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP, WorkflowMode.KNOWLEDGE,
WorkflowMode.KNOWLEDGE_LOOP]
WorkflowMode.KNOWLEDGE_LOOP, WorkflowMode.TOOL, WorkflowMode.TOOL_LOOP]

def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return ImageGenerateNodeSerializer

def _run(self):
if [WorkflowMode.KNOWLEDGE, WorkflowMode.KNOWLEDGE_LOOP].__contains__(
self.workflow_manage.flow.workflow_mode):
if [WorkflowMode.KNOWLEDGE, WorkflowMode.KNOWLEDGE_LOOP, WorkflowMode.TOOL,
WorkflowMode.TOOL_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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ class ImageToVideoNodeSerializer(serializers.Serializer):
class IImageToVideoNode(INode):
type = 'image-to-video-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP, WorkflowMode.KNOWLEDGE,
WorkflowMode.KNOWLEDGE_LOOP]
WorkflowMode.KNOWLEDGE_LOOP, WorkflowMode.TOOL, WorkflowMode.TOOL_LOOP]

def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return ImageToVideoNodeSerializer

Expand All @@ -57,11 +58,12 @@ def _run(self):
if k not in ['first_frame_url', 'last_frame_url']}
if [WorkflowMode.KNOWLEDGE, WorkflowMode.KNOWLEDGE_LOOP].__contains__(
self.workflow_manage.flow.workflow_mode):
return self.execute(first_frame_url=first_frame_url, last_frame_url=last_frame_url, **node_params_data, **self.flow_params_serializer.data,
return self.execute(first_frame_url=first_frame_url, last_frame_url=last_frame_url, **node_params_data,
**self.flow_params_serializer.data,
**{'history_chat_record': [], 'stream': True, 'chat_id': None, 'chat_record_id': None})
else:
return self.execute(first_frame_url=first_frame_url, last_frame_url=last_frame_url,
**node_params_data, **self.flow_params_serializer.data)
**node_params_data, **self.flow_params_serializer.data)

def execute(self, model_id, prompt, negative_prompt, dialogue_number, dialogue_type, history_chat_record,
model_params_setting,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,19 @@ class ImageUnderstandNodeSerializer(serializers.Serializer):
class IImageUnderstandNode(INode):
type = 'image-understand-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP, WorkflowMode.KNOWLEDGE,
WorkflowMode.KNOWLEDGE_LOOP]
WorkflowMode.KNOWLEDGE_LOOP, WorkflowMode.TOOL, WorkflowMode.TOOL_LOOP]

def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return ImageUnderstandNodeSerializer

def _run(self):
res = self.workflow_manage.get_reference_field(self.node_params_serializer.data.get('image_list')[0],
self.node_params_serializer.data.get('image_list')[1:])
if [WorkflowMode.KNOWLEDGE, WorkflowMode.KNOWLEDGE_LOOP].__contains__(
self.workflow_manage.flow.workflow_mode):
if [WorkflowMode.KNOWLEDGE, WorkflowMode.KNOWLEDGE_LOOP, WorkflowMode.TOOL,
WorkflowMode.TOOL_LOOP].__contains__(
self.workflow_manage.flow.workflow_mode):
return self.execute(image=res, **self.node_params_serializer.data, **self.flow_params_serializer.data,
**{'history_chat_record': [], 'stream': True, 'chat_record_id': None})
**{'history_chat_record': [], 'stream': True, 'chat_record_id': None})
else:
return self.execute(image=res, **self.node_params_serializer.data, **self.flow_params_serializer.data)

Expand Down
7 changes: 4 additions & 3 deletions apps/application/flow/step_node/intent_node/i_intent_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class IntentNodeSerializer(serializers.Serializer):
class IIntentNode(INode):
type = 'intent-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP, WorkflowMode.KNOWLEDGE,
WorkflowMode.KNOWLEDGE_LOOP]
WorkflowMode.KNOWLEDGE_LOOP, WorkflowMode.TOOL, WorkflowMode.TOOL_LOOP]

def save_context(self, details, workflow_manage):
pass
Expand All @@ -41,8 +41,9 @@ def _run(self):
self.node_params_serializer.data.get('content_list')[0],
self.node_params_serializer.data.get('content_list')[1:],
)
if [WorkflowMode.KNOWLEDGE, WorkflowMode.KNOWLEDGE_LOOP].__contains__(
self.workflow_manage.flow.workflow_mode):
if [WorkflowMode.KNOWLEDGE, WorkflowMode.KNOWLEDGE_LOOP, WorkflowMode.TOOL,
WorkflowMode.TOOL_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,
'user_input': str(question)})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class LoopBreakNodeSerializer(serializers.Serializer):

class ILoopBreakNode(INode):
type = 'loop-break-node'
support = [WorkflowMode.APPLICATION_LOOP, WorkflowMode.KNOWLEDGE_LOOP]
support = [WorkflowMode.APPLICATION_LOOP, WorkflowMode.KNOWLEDGE_LOOP, WorkflowMode.TOOL_LOOP]

def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return LoopBreakNodeSerializer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class LoopContinueNodeSerializer(serializers.Serializer):

class ILoopContinueNode(INode):
type = 'loop-continue-node'
support = [WorkflowMode.APPLICATION_LOOP, WorkflowMode.KNOWLEDGE_LOOP]
support = [WorkflowMode.APPLICATION_LOOP, WorkflowMode.KNOWLEDGE_LOOP, WorkflowMode.TOOL_LOOP]

def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return LoopContinueNodeSerializer
Expand Down
2 changes: 1 addition & 1 deletion apps/application/flow/step_node/loop_node/i_loop_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def is_valid(self, *, raise_exception=False):

class ILoopNode(INode):
type = 'loop-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.KNOWLEDGE]
support = [WorkflowMode.APPLICATION, WorkflowMode.KNOWLEDGE, WorkflowMode.TOOL]

def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return ILoopNodeSerializer
Expand Down
Loading
Loading