Skip to content

Commit 4c69884

Browse files
authored
fix: tool workflow bugs (#4962)
1 parent dc90be8 commit 4c69884

File tree

10 files changed

+127
-18
lines changed

10 files changed

+127
-18
lines changed

apps/application/flow/i_step_node.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,12 @@ def handler(self, workflow):
150150
source_type=self.chat_info.source_type,
151151
source_id=self.chat_info.source_id,
152152
state=state,
153+
run_time=time.time() - workflow.context.get('start_time') if workflow.context.get(
154+
'start_time') is not None else 0,
153155
meta={
156+
'input_field_list': workflow.get_input_field_list(),
157+
'output_field_list': workflow.get_output_field_list(),
158+
'input': workflow.get_input(),
154159
'output': workflow.out_context,
155160
'details': workflow.get_runtime_details(),
156161
'answer_text_list': workflow.get_answer_text_list()

apps/application/flow/step_node/ai_chat_step_node/impl/base_chat_node.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,15 @@
1313
from functools import reduce
1414
from typing import List, Dict
1515

16+
import uuid_utils.compat as uuid
17+
from django.db.models import QuerySet, OuterRef, Subquery
18+
from django.utils.translation import gettext as _
19+
from langchain_core.messages import BaseMessage, AIMessage, HumanMessage, SystemMessage
1620
from langchain_core.tools import StructuredTool
21+
from pydantic import Field, create_model
1722

1823
from application.flow.common import Workflow, WorkflowMode
19-
from application.flow.i_step_node import NodeResult, INode, ToolWorkflowPostHandler, ToolWorkflowCallPostHandler
24+
from application.flow.i_step_node import NodeResult, INode, ToolWorkflowPostHandler
2025
from application.flow.step_node.ai_chat_step_node.i_chat_node import IChatNode
2126
from application.flow.tools import Reasoning, mcp_response_generator
2227
from application.models import Application, ApplicationApiKey, ApplicationAccessToken
@@ -25,14 +30,9 @@
2530
from common.utils.rsa_util import rsa_long_decrypt
2631
from common.utils.shared_resource_auth import filter_authorized_ids
2732
from common.utils.tool_code import ToolExecutor
28-
from django.db.models import QuerySet, OuterRef, Subquery
29-
from django.utils.translation import gettext as _
30-
from langchain_core.messages import BaseMessage, AIMessage, HumanMessage, SystemMessage
3133
from models_provider.models import Model
3234
from models_provider.tools import get_model_credential, get_model_instance_by_model_workspace_id
3335
from tools.models import Tool, ToolWorkflowVersion, ToolType
34-
from pydantic import BaseModel, Field, create_model
35-
import uuid_utils.compat as uuid
3636

3737

3838
def build_schema(fields: dict):
@@ -66,14 +66,14 @@ def get_workflow_args(tool, qv):
6666
return build_schema({})
6767

6868

69-
def get_workflow_func(tool, qv, workspace_id):
69+
def get_workflow_func(node, tool, qv, workspace_id):
7070
tool_id = tool.id
7171
tool_record_id = str(uuid.uuid7())
7272
took_execute = ToolExecute(tool_id, tool_record_id,
7373
workspace_id,
74-
None,
75-
None,
76-
True)
74+
node.workflow_manage.get_source_type(),
75+
node.workflow_manage.get_source_id(),
76+
False)
7777

7878
def inner(**kwargs):
7979
from application.flow.tool_workflow_manage import ToolWorkflowManage
@@ -86,7 +86,7 @@ def inner(**kwargs):
8686
'workspace_id': workspace_id,
8787
**kwargs},
8888

89-
ToolWorkflowCallPostHandler(took_execute, tool_id),
89+
ToolWorkflowPostHandler(took_execute, tool_id),
9090
is_the_task_interrupted=lambda: False,
9191
child_node=None,
9292
start_node_id=None,
@@ -101,7 +101,7 @@ def inner(**kwargs):
101101
return inner
102102

103103

104-
def get_tools(tool_workflow_ids, workspace_id):
104+
def get_tools(node, tool_workflow_ids, workspace_id):
105105
tools = QuerySet(Tool).filter(id__in=tool_workflow_ids, tool_type=ToolType.WORKFLOW, workspace_id=workspace_id)
106106
latest_subquery = ToolWorkflowVersion.objects.filter(
107107
tool_id=OuterRef('tool_id')
@@ -115,7 +115,7 @@ def get_tools(tool_workflow_ids, workspace_id):
115115
results = []
116116
for tool in tools:
117117
qv = qd.get(tool.id)
118-
func = get_workflow_func(tool, qv, workspace_id)
118+
func = get_workflow_func(node, tool, qv, workspace_id)
119119
args = get_workflow_args(tool, qv)
120120
tool = StructuredTool.from_function(
121121
func=func,
@@ -360,7 +360,7 @@ def _handle_mcp_request(self, mcp_source, mcp_servers, mcp_tool_id, mcp_tool_ids
360360
mcp_servers_config = {**mcp_servers_config, **json.loads(mcp_tool['code'])}
361361
mcp_servers_config = self.handle_variables(mcp_servers_config)
362362
tool_init_params = {}
363-
tools = get_tools(tool_ids, workspace_id)
363+
tools = get_tools(self, tool_ids, workspace_id)
364364
if tool_ids and len(tool_ids) > 0: # 如果有工具ID,则将其转换为MCP
365365
self.context['tool_ids'] = tool_ids
366366
for tool_id in tool_ids:

apps/application/flow/step_node/image_generate_step_node/impl/base_image_generate_node.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
from application.flow.step_node.image_generate_step_node.i_image_generate_node import IImageGenerateNode
1111
from common.utils.common import bytes_to_uploaded_file
1212
from knowledge.models import FileSourceType
13-
from oss.serializers.file import FileSerializer
1413
from models_provider.tools import get_model_instance_by_model_workspace_id
14+
from oss.serializers.file import FileSerializer
1515

1616

1717
class BaseImageGenerateNode(IImageGenerateNode):
@@ -117,6 +117,8 @@ def upload_file(self, file):
117117
if [WorkflowMode.KNOWLEDGE, WorkflowMode.KNOWLEDGE_LOOP].__contains__(
118118
self.workflow_manage.flow.workflow_mode):
119119
return self.upload_knowledge_file(file)
120+
if [WorkflowMode.TOOL, WorkflowMode.TOOL_LOOP].__contains__(self.workflow_manage.flow.workflow_mode):
121+
return self.upload_tool_file(file)
120122
return self.upload_application_file(file)
121123

122124
def upload_knowledge_file(self, file):
@@ -133,6 +135,20 @@ def upload_knowledge_file(self, file):
133135
}).upload()
134136
return file_url
135137

138+
def upload_tool_file(self, file):
139+
tool_id = self.workflow_params.get('tool_id')
140+
meta = {
141+
'debug': False,
142+
'tool_id': tool_id,
143+
}
144+
file_url = FileSerializer(data={
145+
'file': file,
146+
'meta': meta,
147+
'source_id': tool_id,
148+
'source_type': FileSourceType.TOOL.value
149+
}).upload()
150+
return file_url
151+
136152
def upload_application_file(self, file):
137153
application = self.workflow_manage.work_flow_post_handler.chat_info.application
138154
chat_id = self.workflow_params.get('chat_id')

apps/application/flow/step_node/image_to_video_step_node/impl/base_image_to_video_node.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ def upload_file(self, file):
9494
if [WorkflowMode.KNOWLEDGE, WorkflowMode.KNOWLEDGE_LOOP].__contains__(
9595
self.workflow_manage.flow.workflow_mode):
9696
return self.upload_knowledge_file(file)
97+
if [WorkflowMode.TOOL, WorkflowMode.TOOL_LOOP].__contains__(self.workflow_manage.flow.workflow_mode):
98+
return self.upload_tool_file(file)
9799
return self.upload_application_file(file)
98100

99101
def upload_knowledge_file(self, file):
@@ -110,6 +112,20 @@ def upload_knowledge_file(self, file):
110112
}).upload()
111113
return file_url
112114

115+
def upload_tool_file(self, file):
116+
tool_id = self.workflow_params.get('tool_id')
117+
meta = {
118+
'debug': False,
119+
'tool_id': tool_id,
120+
}
121+
file_url = FileSerializer(data={
122+
'file': file,
123+
'meta': meta,
124+
'source_id': tool_id,
125+
'source_type': FileSourceType.TOOL.value
126+
}).upload()
127+
return file_url
128+
113129
def upload_application_file(self, file):
114130
application = self.workflow_manage.work_flow_post_handler.chat_info.application
115131
chat_id = self.workflow_params.get('chat_id')

apps/application/flow/step_node/question_node/i_question_node.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,12 @@ def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
4242
return QuestionNodeSerializer
4343

4444
def _run(self):
45-
return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data)
45+
if [WorkflowMode.KNOWLEDGE, WorkflowMode.KNOWLEDGE_LOOP, WorkflowMode.TOOL,
46+
WorkflowMode.TOOL_LOOP].__contains__(self.workflow_manage.flow.workflow_mode):
47+
return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data,
48+
**{'history_chat_record': [], 'stream': True, 'chat_id': None, 'chat_record_id': None})
49+
else:
50+
return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data)
4651

4752
def execute(self, model_id, system, prompt, dialogue_number, history_chat_record, stream, chat_id, chat_record_id,
4853
model_params_setting=None, model_id_type=None, model_id_reference=None,

apps/application/flow/step_node/text_to_speech_step_node/impl/base_text_to_speech_node.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ def upload_file(self, file):
111111
if [WorkflowMode.KNOWLEDGE, WorkflowMode.KNOWLEDGE_LOOP].__contains__(
112112
self.workflow_manage.flow.workflow_mode):
113113
return self.upload_knowledge_file(file)
114+
if [WorkflowMode.TOOL, WorkflowMode.TOOL_LOOP].__contains__(self.workflow_manage.flow.workflow_mode):
115+
return self.upload_tool_file(file)
114116
return self.upload_application_file(file)
115117

116118
def upload_knowledge_file(self, file):
@@ -127,6 +129,20 @@ def upload_knowledge_file(self, file):
127129
}).upload()
128130
return file_url
129131

132+
def upload_tool_file(self, file):
133+
tool_id = self.workflow_params.get('tool_id')
134+
meta = {
135+
'debug': False,
136+
'tool_id': tool_id,
137+
}
138+
file_url = FileSerializer(data={
139+
'file': file,
140+
'meta': meta,
141+
'source_id': tool_id,
142+
'source_type': FileSourceType.TOOL.value
143+
}).upload()
144+
return file_url
145+
130146
def upload_application_file(self, file):
131147
application = self.workflow_manage.work_flow_post_handler.chat_info.application
132148
chat_id = self.workflow_params.get('chat_id')

apps/application/flow/step_node/text_to_video_step_node/impl/base_text_to_video_node.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ def upload_file(self, file):
6868
if [WorkflowMode.KNOWLEDGE, WorkflowMode.KNOWLEDGE_LOOP].__contains__(
6969
self.workflow_manage.flow.workflow_mode):
7070
return self.upload_knowledge_file(file)
71+
if [WorkflowMode.TOOL, WorkflowMode.TOOL_LOOP].__contains__(self.workflow_manage.flow.workflow_mode):
72+
return self.upload_tool_file(file)
7173
return self.upload_application_file(file)
7274

7375
def upload_knowledge_file(self, file):
@@ -84,6 +86,20 @@ def upload_knowledge_file(self, file):
8486
}).upload()
8587
return file_url
8688

89+
def upload_tool_file(self, file):
90+
tool_id = self.workflow_params.get('tool_id')
91+
meta = {
92+
'debug': False,
93+
'tool_id': tool_id,
94+
}
95+
file_url = FileSerializer(data={
96+
'file': file,
97+
'meta': meta,
98+
'source_id': tool_id,
99+
'source_type': FileSourceType.TOOL.value
100+
}).upload()
101+
return file_url
102+
87103
def upload_application_file(self, file):
88104
application = self.workflow_manage.work_flow_post_handler.chat_info.application
89105
chat_id = self.workflow_params.get('chat_id')

apps/application/flow/step_node/tool_start_node/impl/base_tool_start_node.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def execute(self, **kwargs) -> NodeResult:
3434
global_value = {}
3535
params = self.workflow_manage.get_body()
3636
for item in base_node.properties.get('user_input_field_list', []):
37-
global_value[item.get('field')] = params[item.get('field')]
37+
global_value[item.get('field')] = params.get(item.get('field'))
3838

3939
self.workflow_manage.out_context = {
4040
item.get('field'): None
@@ -48,7 +48,7 @@ def get_details(self, index: int, **kwargs):
4848
for field in self.node.properties.get('config')['globalFields']:
4949
key = field['value']
5050
global_fields.append({
51-
'label': field['label'],
51+
'label': field.get('label'),
5252
'key': key,
5353
'value': self.workflow_manage.context[key] if key in self.workflow_manage.context else ''
5454
})

apps/application/flow/tool_workflow_manage.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
@date:2026/3/12 15:17
77
@desc:
88
"""
9+
import time
910
from concurrent.futures import ThreadPoolExecutor
1011

1112
from django.db import close_old_connections
@@ -32,6 +33,14 @@ def __init__(self, flow: Workflow, params, work_flow_post_handler: WorkFlowPostH
3233
def get_params_serializer_class(self):
3334
return ToolFlowParamsSerializer
3435

36+
def run(self):
37+
self.context['start_time'] = time.time()
38+
close_old_connections()
39+
language = get_language()
40+
if self.params.get('stream'):
41+
return self.run_stream(self.start_node, None, language)
42+
return self.run_block(language)
43+
3544
def stream(self):
3645
close_old_connections()
3746
language = get_language()
@@ -48,6 +57,30 @@ def get_base_node(self):
4857
"""
4958
return self.flow.get_node('tool-base-node')
5059

60+
def get_input_field_list(self):
61+
"""
62+
获取输入字段列表
63+
@return: 输入字段配置
64+
"""
65+
base_node = self.get_base_node()
66+
return base_node.properties.get("user_input_field_list") or []
67+
68+
def get_output_field_list(self):
69+
"""
70+
获取输出字段列表配置
71+
@return: 输出字段列表配置
72+
"""
73+
base_node = self.get_base_node()
74+
return base_node.properties.get("user_output_field_list") or []
75+
76+
def get_input(self):
77+
"""
78+
获取用户输入
79+
@return: 用户输入
80+
"""
81+
input_field_list = self.get_input_field_list()
82+
return {f.get('field'): self.params.get(f.get('field')) for f in input_field_list}
83+
5184
def get_source_type(self):
5285
return "TOOL"
5386

apps/application/serializers/common.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ def set_record(self, tool_record):
7878
QuerySet(ToolRecord).update_or_create(id=tool_record.id,
7979
create_defaults={'id': tool_record.id,
8080
'tool_id': tool_record.tool_id,
81+
'state': tool_record.state,
8182
'workspace_id': tool_record.workspace_id,
8283
"source_type": tool_record.source_type,
8384
'source_id': tool_record.source_id,
@@ -88,6 +89,7 @@ def set_record(self, tool_record):
8889
'tool_id': tool_record.tool_id,
8990
"source_type": tool_record.source_type,
9091
'source_id': tool_record.source_id,
92+
'state': tool_record.state,
9193
'meta': tool_record.meta,
9294
'run_time': tool_record.run_time
9395
})

0 commit comments

Comments
 (0)