Skip to content

Commit e9ea594

Browse files
authored
fix: Verify cyclic dependencies when saving tool workflows (#5059)
1 parent 3688510 commit e9ea594

4 files changed

Lines changed: 53 additions & 5 deletions

File tree

apps/locales/en_US/LC_MESSAGES/django.po

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9236,6 +9236,5 @@ msgstr "Batch delete"
92369236
msgid "Batch move"
92379237
msgstr "Batch move"
92389238

9239-
9240-
9241-
9239+
msgid "There is a circular dependency in the tool workflow"
9240+
msgstr "There is a circular dependency in the tool workflow"

apps/locales/zh_CN/LC_MESSAGES/django.po

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9358,3 +9358,6 @@ msgstr "批量删除"
93589358

93599359
msgid "Batch move"
93609360
msgstr "批量移动"
9361+
9362+
msgid "There is a circular dependency in the tool workflow"
9363+
msgstr "工具工作流存在循环依赖"

apps/locales/zh_Hant/LC_MESSAGES/django.po

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9356,4 +9356,5 @@ msgstr "批量刪除"
93569356
msgid "Batch move"
93579357
msgstr "批量移動"
93589358

9359-
9359+
msgid "There is a circular dependency in the tool workflow"
9360+
msgstr "工具工作流存在迴圈依賴"

apps/tools/serializers/tool_workflow.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from django.db.models import QuerySet, Q
2323
from django.http import HttpResponse
2424
from django.utils import timezone
25-
from django.utils.translation import gettext_lazy as _
25+
from django.utils.translation import gettext_lazy as _, gettext
2626
from rest_framework import serializers, status
2727
from rest_framework.utils.formatting import lazy_format
2828

@@ -52,6 +52,47 @@
5252
tool_executor = ToolExecutor()
5353

5454

55+
def is_valid_tool_workflow_circular_dependency(workflow, _id, visited=None, stack=None):
56+
"""
57+
workflow: 当前要检查的 workflow 对象
58+
visited: 全局已经访问过的 workflow id
59+
stack: 当前递归栈里的 workflow id
60+
"""
61+
if visited is None:
62+
visited = set()
63+
if stack is None:
64+
stack = set()
65+
66+
if _id in stack:
67+
return False
68+
69+
if _id in visited:
70+
return True
71+
72+
stack.add(_id)
73+
74+
for node in workflow.get('nodes', []):
75+
child_tool_ids = []
76+
if node.get('type') == 'ai-chat-node':
77+
node_data = node.get('properties', {}).get('node_data', {})
78+
child_tool_ids = node_data.get('tool_ids') or []
79+
if node.get('type') == 'tool-workflow-lib-node':
80+
child_tool_id = node.get('properties', {}).get('node_data', {}).get('tool_lib_id')
81+
child_tool_ids.append(child_tool_id)
82+
for child_tool_id in child_tool_ids:
83+
if child_tool_id:
84+
child_workflow = QuerySet(ToolWorkflow).filter(tool_id=child_tool_id).first()
85+
if child_workflow:
86+
if not is_valid_tool_workflow_circular_dependency(child_workflow.work_flow, str(child_tool_id),
87+
visited,
88+
stack):
89+
return False
90+
91+
stack.remove(_id)
92+
visited.add(_id)
93+
return True
94+
95+
5596
def hand_node(node, update_tool_map):
5697
if node.get('type') == 'tool-lib-node':
5798
tool_lib_id = (node.get('properties', {}).get('node_data', {}).get('tool_lib_id') or '')
@@ -233,6 +274,10 @@ def edit(self, instance: Dict):
233274
tool = QuerySet(Tool).filter(id=self.data.get("tool_id")).first()
234275
workflow_id = tool.workspace_id
235276
if instance.get("work_flow"):
277+
dependency = is_valid_tool_workflow_circular_dependency(workflow=instance.get('work_flow'),
278+
_id=str(tool.id))
279+
if not dependency:
280+
raise Exception(gettext('There is a circular dependency in the tool workflow'))
236281
QuerySet(ToolWorkflow).update_or_create(tool_id=self.data.get("tool_id"),
237282
create_defaults={'id': uuid.uuid7(),
238283
'tool_id': self.data.get(

0 commit comments

Comments
 (0)