diff --git a/api/app/core/workflow/executor.py b/api/app/core/workflow/executor.py
index 6721d7b0..b719091c 100644
--- a/api/app/core/workflow/executor.py
+++ b/api/app/core/workflow/executor.py
@@ -133,7 +133,7 @@ class WorkflowExecutor:
for node in self.workflow_config.get("nodes")
if node.get("type") in [NodeType.LOOP, NodeType.ITERATION]
], # loop, iteration node id
- "looping": False, # loop runing flag, only use in loop node,not use in main loop
+ "looping": 0, # loop runing flag, only use in loop node,not use in main loop
"activate": {
self.start_node_id: True
}
@@ -358,6 +358,7 @@ class WorkflowExecutor:
elif mode == "updates":
# Handle state updates - store final state
+ # TODO:流式输出点
logger.debug(f"[UPDATES] 收到 state 更新 from {list(data.keys())} "
f"- execution_id: {self.execution_id}")
diff --git a/api/app/core/workflow/nodes/base_node.py b/api/app/core/workflow/nodes/base_node.py
index d8311a16..1ebeb378 100644
--- a/api/app/core/workflow/nodes/base_node.py
+++ b/api/app/core/workflow/nodes/base_node.py
@@ -19,13 +19,17 @@ from app.core.workflow.variable_pool import VariablePool
logger = logging.getLogger(__name__)
-def merget_activate_state(x, y):
+def merge_activate_state(x, y):
return {
k: x.get(k, False) or y.get(k, False)
for k in set(x) | set(y)
}
+def merge_looping_state(x, y):
+ return y if y > x else x
+
+
class WorkflowState(TypedDict):
"""Workflow state
@@ -36,7 +40,7 @@ class WorkflowState(TypedDict):
# Set of loop node IDs, used for assigning values in loop nodes
cycle_nodes: list
- looping: Annotated[bool, lambda x, y: x and y]
+ looping: Annotated[int, merge_looping_state]
# Input variables (passed from configured variables)
# Uses a deep merge function, supporting nested dict updates (e.g., conv.xxx)
@@ -68,7 +72,7 @@ class WorkflowState(TypedDict):
streaming_buffer: Annotated[dict[str, Any], lambda x, y: {**x, **y}]
# node activate status
- activate: Annotated[dict[str, bool], merget_activate_state]
+ activate: Annotated[dict[str, bool], merge_activate_state]
class BaseNode(ABC):
diff --git a/api/app/core/workflow/nodes/breaker/node.py b/api/app/core/workflow/nodes/breaker/node.py
index 882ffda0..f00015d1 100644
--- a/api/app/core/workflow/nodes/breaker/node.py
+++ b/api/app/core/workflow/nodes/breaker/node.py
@@ -28,6 +28,6 @@ class BreakNode(BaseNode):
Returns:
Optional dictionary indicating the loop has been stopped.
"""
- state["looping"] = False
+ state["looping"] = 2
logger.info(f"Setting cycle node exit flag, cycle={self.cycle}, looping={state['looping']}")
diff --git a/api/app/core/workflow/nodes/cycle_graph/iteration.py b/api/app/core/workflow/nodes/cycle_graph/iteration.py
index da093864..e9174df8 100644
--- a/api/app/core/workflow/nodes/cycle_graph/iteration.py
+++ b/api/app/core/workflow/nodes/cycle_graph/iteration.py
@@ -58,10 +58,10 @@ class IterationRuntime:
idx: Index of the element in the input array.
Returns:
- A deep copy of the workflow state with iteration-specific variables set.
+ A copy of the workflow state with iteration-specific variables set.
"""
loopstate = WorkflowState(
- **copy.deepcopy(self.state)
+ **self.state
)
loopstate["runtime_vars"][self.node_id] = {
"item": item,
@@ -71,7 +71,7 @@ class IterationRuntime:
"item": item,
"index": idx,
}
- loopstate["looping"] = True
+ loopstate["looping"] = 1
loopstate["activate"][self.start_id] = True
return loopstate
@@ -89,7 +89,7 @@ class IterationRuntime:
self.result.extend(output)
else:
self.result.append(output)
- if not result["looping"]:
+ if result["looping"] == 2:
self.looping = False
return result
@@ -150,10 +150,9 @@ class IterationRuntime:
self.result.extend(output)
else:
self.result.append(output)
- if not result["looping"]:
+ if result["looping"] == 2:
self.looping = False
idx += 1
-
logger.info(f"Iteration node {self.node_id}: execution completed")
return {
"output": self.result,
diff --git a/api/app/core/workflow/nodes/cycle_graph/loop.py b/api/app/core/workflow/nodes/cycle_graph/loop.py
index c5dc5457..6a15891f 100644
--- a/api/app/core/workflow/nodes/cycle_graph/loop.py
+++ b/api/app/core/workflow/nodes/cycle_graph/loop.py
@@ -46,6 +46,7 @@ class LoopRuntime:
self.state = state
self.node_id = node_id
self.typed_config = LoopNodeConfig(**config)
+ self.looping = True
def _init_loop_state(self):
"""
@@ -88,7 +89,7 @@ class LoopRuntime:
loopstate = WorkflowState(
**self.state
)
- loopstate["looping"] = True
+ loopstate["looping"] = 1
loopstate["activate"][self.start_id] = True
return loopstate
@@ -179,9 +180,12 @@ class LoopRuntime:
loopstate = self._init_loop_state()
loop_time = self.typed_config.max_loop
child_state = []
- while self.evaluate_conditional(loopstate) and loopstate["looping"] and loop_time > 0:
+ while self.evaluate_conditional(loopstate) and self.looping and loop_time > 0:
logger.info(f"loop node {self.node_id}: running")
- child_state.append(await self.graph.ainvoke(loopstate))
+ result = await self.graph.ainvoke(loopstate)
+ child_state.append(result)
+ if result["looping"] == 2:
+ self.looping = False
loop_time -= 1
logger.info(f"loop node {self.node_id}: execution completed")
diff --git a/web/src/views/Index/components/VersionCard.tsx b/web/src/views/Index/components/VersionCard.tsx
index b299ad29..702b6520 100644
--- a/web/src/views/Index/components/VersionCard.tsx
+++ b/web/src/views/Index/components/VersionCard.tsx
@@ -4,7 +4,7 @@
* @Author: yujiangping
* @Date: 2026-01-12 16:34:59
* @LastEditors: yujiangping
- * @LastEditTime: 2026-01-16 15:38:35
+ * @LastEditTime: 2026-01-23 19:07:36
*/
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -23,6 +23,13 @@ const GuideCard: React.FC = () => {
return currentLang === 'zh' ? versionInfo.introduction : (versionInfo.introduction_en || versionInfo.introduction);
};
+ // 解析换行符和HTML的方法
+ const parseContent = (text: string) => {
+ if (!text) return '';
+ // 将 \n 转换为
标签
+ return text.replace(/\\n/g, '
');
+ };
+
useEffect(() => {
const fetchVersion = async () => {
try {
@@ -58,13 +65,16 @@ const GuideCard: React.FC = () => {
{t('version.name')}: {introduction.codeName}
-
- {introduction.upgradePosition} -
+ {introduction.coreUpgrades?.map((item: string, index: number) => ( -- {index + 1}. {item} -
+ ))} >) : null; })()}