Skip to content

Commit bfb03a6

Browse files
committed
feat: loopNode
1 parent 5837650 commit bfb03a6

File tree

6 files changed

+144
-35
lines changed

6 files changed

+144
-35
lines changed

apps/application/flow/step_node/loop_node/impl/base_loop_node.py

Lines changed: 99 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ def write_context_stream(node_variable: Dict, workflow_variable: Dict, node: INo
3232
@param node: 节点
3333
@param workflow: 工作流管理器
3434
"""
35+
3536
response = node_variable.get('result')
37+
workflow_manage = node_variable.get('workflow_manage')
3638
answer = ''
3739
reasoning_content = ''
3840
for chunk in response:
@@ -42,7 +44,7 @@ def write_context_stream(node_variable: Dict, workflow_variable: Dict, node: INo
4244
answer += content_chunk
4345
yield {'content': content_chunk,
4446
'reasoning_content': reasoning_content_chunk}
45-
47+
runtime_details = workflow_manage.get_runtime_details()
4648
_write_context(node_variable, workflow_variable, node, workflow, answer, reasoning_content)
4749

4850

@@ -69,28 +71,53 @@ def write_context(node_variable: Dict, workflow_variable: Dict, node: INode, wor
6971
_write_context(node_variable, workflow_variable, node, workflow, content, reasoning_content)
7072

7173

72-
def loop_number(number, loop_body):
73-
"""
74-
指定次数循环
75-
@return:
76-
"""
77-
pass
78-
79-
80-
def loop_array(array, loop_body):
81-
"""
82-
循环数组
83-
@return:
84-
"""
85-
pass
86-
87-
88-
def loop_loop(loop_body):
89-
"""
90-
无线循环
91-
@return:
92-
"""
93-
pass
74+
def loop_number(number: int, loop_body, workflow_manage_new_instance, workflow):
75+
loop_global_data = {}
76+
for index in range(number):
77+
"""
78+
指定次数循环
79+
@return:
80+
"""
81+
instance = workflow_manage_new_instance({'index': index}, loop_global_data)
82+
response = instance.stream()
83+
answer = ''
84+
reasoning_content = ''
85+
for chunk in response:
86+
content_chunk = chunk.get('content', '')
87+
reasoning_content_chunk = chunk.get('reasoning_content', '')
88+
reasoning_content += reasoning_content_chunk
89+
answer += content_chunk
90+
yield chunk
91+
loop_global_data = instance.context
92+
93+
94+
def loop_array(array, loop_body, workflow_manage_new_instance, workflow):
95+
loop_global_data = {}
96+
for item, index in zip(array, range(len(array))):
97+
"""
98+
指定次数循环
99+
@return:
100+
"""
101+
instance = workflow_manage_new_instance({'index': index, 'item': item}, loop_global_data)
102+
response = instance.stream()
103+
answer = ''
104+
reasoning_content = ''
105+
for chunk in response:
106+
content_chunk = chunk.get('content', '')
107+
reasoning_content_chunk = chunk.get('reasoning_content', '')
108+
reasoning_content += reasoning_content_chunk
109+
answer += content_chunk
110+
yield chunk
111+
loop_global_data = instance.context
112+
113+
114+
def get_write_context(loop_type, array, number, loop_body, stream):
115+
def inner_write_context(node_variable: Dict, workflow_variable: Dict, node: INode, workflow):
116+
if loop_type == 'ARRAY':
117+
return loop_array(array, loop_body, node_variable['workflow_manage_new_instance'], workflow)
118+
return loop_number(number, loop_body, node_variable['workflow_manage_new_instance'], workflow)
119+
120+
return inner_write_context
94121

95122

96123
class LoopWorkFlowPostHandler(WorkFlowPostHandler):
@@ -108,14 +135,55 @@ def save_context(self, details, workflow_manage):
108135

109136
def execute(self, loop_type, array, number, loop_body, stream, **kwargs) -> NodeResult:
110137
from application.flow.workflow_manage import WorkflowManage, Flow
111-
workflow_manage = WorkflowManage(Flow.new_instance(loop_body), self.workflow_manage.params,
112-
LoopWorkFlowPostHandler(self.workflow_manage.work_flow_post_handler.chat_info
113-
,
114-
self.workflow_manage.work_flow_post_handler.client_id,
115-
self.workflow_manage.work_flow_post_handler.client_type)
116-
, base_to_response=LoopToResponse())
117-
result = workflow_manage.stream()
118-
return NodeResult({"result": result}, {}, _write_context=write_context_stream)
138+
def workflow_manage_new_instance(start_data, global_data):
139+
workflow_manage = WorkflowManage(Flow.new_instance(loop_body), self.workflow_manage.params,
140+
LoopWorkFlowPostHandler(
141+
self.workflow_manage.work_flow_post_handler.chat_info
142+
,
143+
self.workflow_manage.work_flow_post_handler.client_id,
144+
self.workflow_manage.work_flow_post_handler.client_type)
145+
, base_to_response=LoopToResponse(),
146+
start_data=start_data,
147+
form_data=global_data)
148+
149+
return workflow_manage
150+
151+
return NodeResult({'workflow_manage_new_instance': workflow_manage_new_instance}, {},
152+
_write_context=get_write_context(loop_type, array, number, loop_body, stream))
153+
154+
def loop_number(self, number: int, loop_body, stream):
155+
for index in range(number):
156+
"""
157+
指定次数循环
158+
@return:
159+
"""
160+
from application.flow.workflow_manage import WorkflowManage, Flow
161+
workflow_manage = WorkflowManage(Flow.new_instance(loop_body), self.workflow_manage.params,
162+
LoopWorkFlowPostHandler(
163+
self.workflow_manage.work_flow_post_handler.chat_info
164+
,
165+
self.workflow_manage.work_flow_post_handler.client_id,
166+
self.workflow_manage.work_flow_post_handler.client_type)
167+
, base_to_response=LoopToResponse(),
168+
start_data={'index': index})
169+
result = workflow_manage.stream()
170+
return NodeResult({"result": result, "workflow_manage": workflow_manage}, {},
171+
_write_context=write_context_stream)
172+
pass
173+
174+
def loop_array(self, array, loop_body, stream):
175+
"""
176+
循环数组
177+
@return:
178+
"""
179+
pass
180+
181+
def loop_loop(self, loop_body, stream):
182+
"""
183+
无线循环
184+
@return:
185+
"""
186+
pass
119187

120188
def get_details(self, index: int, **kwargs):
121189
return {

apps/application/flow/step_node/start_node/impl/base_start_node.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ def execute(self, question, **kwargs) -> NodeResult:
6464
'question': question,
6565
'image': self.workflow_manage.image_list,
6666
'document': self.workflow_manage.document_list,
67-
'audio': self.workflow_manage.audio_list
67+
'audio': self.workflow_manage.audio_list,
68+
**self.workflow_manage.start_data
69+
6870
}
6971
return NodeResult(node_variable, workflow_variable)
7072

apps/application/flow/workflow_manage.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,11 @@ def __init__(self, flow: Flow, params, work_flow_post_handler: WorkFlowPostHandl
239239
document_list=None,
240240
audio_list=None,
241241
start_node_id=None,
242-
start_node_data=None, chat_record=None, child_node=None):
242+
start_node_data=None, chat_record=None, child_node=None, start_data=None):
243243
if form_data is None:
244244
form_data = {}
245+
if start_data is None:
246+
start_data = {}
245247
if image_list is None:
246248
image_list = []
247249
if document_list is None:
@@ -272,6 +274,7 @@ def __init__(self, flow: Flow, params, work_flow_post_handler: WorkFlowPostHandl
272274
self.field_list = []
273275
self.global_field_list = []
274276
self.init_fields()
277+
self.start_data = start_data
275278
if start_node_id is not None:
276279
self.load_node(chat_record, start_node_id, start_node_data)
277280
else:

ui/src/workflow/common/data.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,34 @@ export const startNode = {
3535
showNode: true
3636
}
3737
}
38+
export const loopStartNode = {
39+
id: WorkflowType.Start,
40+
type: WorkflowType.Start,
41+
x: 480,
42+
y: 3340,
43+
properties: {
44+
height: 364,
45+
stepName: t('views.applicationWorkflow.nodes.startNode.label'),
46+
config: {
47+
fields: [
48+
{
49+
label: t('views.applicationWorkflow.nodes.startNode.index', '下标'),
50+
value: 'index'
51+
},
52+
{
53+
label: t('views.applicationWorkflow.nodes.startNode.item', '循环元素'),
54+
value: 'item'
55+
}
56+
],
57+
globalFields: []
58+
},
59+
fields: [{ label: t('views.applicationWorkflow.nodes.startNode.question'), value: 'question' }],
60+
globalFields: [
61+
{ label: t('views.applicationWorkflow.nodes.startNode.currentTime'), value: 'time' }
62+
],
63+
showNode: true
64+
}
65+
}
3866
export const baseNode = {
3967
id: WorkflowType.Base,
4068
type: WorkflowType.Base,

ui/src/workflow/nodes/loop-body-node/index.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ const renderGraphData = (data?: any) => {
7979
lf.value.graphModel.eventCenter.on('history:change', (data: any) => {
8080
set(props.nodeModel.properties, 'workflow', lf.value.getGraphData())
8181
})
82+
83+
lf.value.graphModel.eventCenter.on('loop:change', (data: any) => {
84+
console.log('xx')
85+
})
8286
setTimeout(() => {
8387
lf.value?.fitView()
8488
}, 500)

ui/src/workflow/nodes/loop-node/index.vue

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ import { set } from 'lodash'
8787
import NodeContainer from '@/workflow/common/NodeContainer.vue'
8888
import { ref, computed, onMounted } from 'vue'
8989
import { isLastNode } from '@/workflow/common/data'
90-
import { loopBodyNode } from '@/workflow/common/data'
90+
import { loopBodyNode, loopStartNode } from '@/workflow/common/data'
9191
import NodeCascader from '@/workflow/common/NodeCascader.vue'
9292
const props = defineProps<{ nodeModel: any }>()
9393
@@ -131,11 +131,15 @@ onMounted(() => {
131131
set(props.nodeModel, 'validate', validate)
132132
const nodeOutgoingNode = props.nodeModel.graphModel.getNodeOutgoingNode(props.nodeModel.id)
133133
if (!nodeOutgoingNode.some((item: any) => item.type == loopBodyNode.type)) {
134+
let workflow = { nodes: [loopStartNode], edges: [] }
135+
if (props.nodeModel.properties.node_data.loop_body) {
136+
workflow = props.nodeModel.properties.node_data.loop_body
137+
}
134138
const nodeModel = props.nodeModel.graphModel.addNode({
135139
type: loopBodyNode.type,
136140
properties: {
137141
...loopBodyNode.properties,
138-
workflow: props.nodeModel.properties.node_data.loop_body,
142+
workflow: workflow,
139143
loop_node_id: props.nodeModel.id
140144
},
141145
x: props.nodeModel.x,

0 commit comments

Comments
 (0)