diff --git a/api/app/core/workflow/nodes/base_node.py b/api/app/core/workflow/nodes/base_node.py index 0c015c89..d8311a16 100644 --- a/api/app/core/workflow/nodes/base_node.py +++ b/api/app/core/workflow/nodes/base_node.py @@ -9,7 +9,6 @@ import logging from abc import ABC, abstractmethod from typing import Any, AsyncGenerator -from langchain_core.messages import AIMessage from langgraph.config import get_stream_writer from typing_extensions import TypedDict, Annotated diff --git a/api/app/core/workflow/nodes/cycle_graph/iteration.py b/api/app/core/workflow/nodes/cycle_graph/iteration.py index 66c3a700..da093864 100644 --- a/api/app/core/workflow/nodes/cycle_graph/iteration.py +++ b/api/app/core/workflow/nodes/cycle_graph/iteration.py @@ -24,6 +24,7 @@ class IterationRuntime: def __init__( self, + start_id: str, graph: CompiledStateGraph, node_id: str, config: dict[str, Any], @@ -38,6 +39,7 @@ class IterationRuntime: config: Dictionary containing iteration node configuration. state: Current workflow state at the point of iteration. """ + self.start_id = start_id self.graph = graph self.state = state self.node_id = node_id @@ -70,6 +72,7 @@ class IterationRuntime: "index": idx, } loopstate["looping"] = True + loopstate["activate"][self.start_id] = True return loopstate async def run_task(self, item, idx): diff --git a/api/app/core/workflow/nodes/cycle_graph/loop.py b/api/app/core/workflow/nodes/cycle_graph/loop.py index 38d4b21c..c5dc5457 100644 --- a/api/app/core/workflow/nodes/cycle_graph/loop.py +++ b/api/app/core/workflow/nodes/cycle_graph/loop.py @@ -26,6 +26,7 @@ class LoopRuntime: def __init__( self, + start_id: str, graph: CompiledStateGraph, node_id: str, config: dict[str, Any], @@ -40,6 +41,7 @@ class LoopRuntime: config: Raw configuration dictionary for the loop node. state: The current workflow state before entering the loop. """ + self.start_id = start_id self.graph = graph self.state = state self.node_id = node_id @@ -87,6 +89,7 @@ class LoopRuntime: **self.state ) loopstate["looping"] = True + loopstate["activate"][self.start_id] = True return loopstate @staticmethod diff --git a/api/app/core/workflow/nodes/cycle_graph/node.py b/api/app/core/workflow/nodes/cycle_graph/node.py index 1659395e..1f550b0b 100644 --- a/api/app/core/workflow/nodes/cycle_graph/node.py +++ b/api/app/core/workflow/nodes/cycle_graph/node.py @@ -34,7 +34,6 @@ class CycleGraphNode(BaseNode): self.cycle_nodes = list() # Nodes belonging to this cycle self.cycle_edges = list() # Edges connecting nodes within the cycle self.start_node_id = None # ID of the start node within the cycle - self.end_node_ids = [] # IDs of end nodes within the cycle self.graph: StateGraph | CompiledStateGraph | None = None self.build_graph() @@ -105,13 +104,15 @@ class CycleGraphNode(BaseNode): """ from app.core.workflow.graph_builder import GraphBuilder self.cycle_nodes, self.cycle_edges = self.pure_cycle_graph() - self.graph = GraphBuilder( + builder = GraphBuilder( { "nodes": self.cycle_nodes, "edges": self.cycle_edges, }, subgraph=True - ).build() + ) + self.start_node_id = builder.start_node_id + self.graph = builder.build() async def execute(self, state: WorkflowState) -> Any: """ @@ -132,6 +133,7 @@ class CycleGraphNode(BaseNode): """ if self.node_type == NodeType.LOOP: return await LoopRuntime( + start_id=self.start_node_id, graph=self.graph, node_id=self.node_id, config=self.config, @@ -139,6 +141,7 @@ class CycleGraphNode(BaseNode): ).run() if self.node_type == NodeType.ITERATION: return await IterationRuntime( + start_id=self.start_node_id, graph=self.graph, node_id=self.node_id, config=self.config, diff --git a/web/src/utils/request.ts b/web/src/utils/request.ts index e49fcb3c..479fc1f3 100644 --- a/web/src/utils/request.ts +++ b/web/src/utils/request.ts @@ -122,6 +122,11 @@ service.interceptors.response.use( } }, (error) => { + // 如果是取消请求,不显示错误提示 + if (axios.isCancel(error) || error.name === 'AbortError' || error.code === 'ERR_CANCELED') { + return Promise.reject(error); + } + // 处理网络错误、超时等 let msg = error.response?.data?.error || error.response?.error; const status = error?.response ? error.response.status : error; diff --git a/web/src/views/KnowledgeBase/[knowledgeBaseId]/CreateDataset.tsx b/web/src/views/KnowledgeBase/[knowledgeBaseId]/CreateDataset.tsx index 117689c1..f876c51b 100644 --- a/web/src/views/KnowledgeBase/[knowledgeBaseId]/CreateDataset.tsx +++ b/web/src/views/KnowledgeBase/[knowledgeBaseId]/CreateDataset.tsx @@ -261,15 +261,15 @@ const CreateDataset = () => { dataIndex: 'progress', key: 'progress', render: (value: number, record: any) => { - // value = 1 时完成,0~1 时显示进度条 - if (value === 1) { + // value >= 1 时完成,0~1 时显示进度条 + if (value >= 1) { return ( {t('knowledgeBase.completed')} ); - } else if (value > 0 && value < 1) { + } else if (value >= 0 && value < 1) { // 处理中,显示进度条 return (
@@ -277,7 +277,10 @@ const CreateDataset = () => { percent={Math.round(value * 100)} size="small" status="active" - strokeColor="#1677ff" + strokeColor={{ + '0%': '#108ee9', + '100%': '#87d068', + }} style={{ width: '120px' }} />
@@ -383,6 +386,9 @@ const CreateDataset = () => { }, }) .then((res: UploadFileResponse) => { + // 上传成功,移除 AbortController + abortControllersRef.current.delete(fileUid); + onSuccess?.(res, new XMLHttpRequest()); if (res?.id) { setRechunkFileIds((prev) => { @@ -578,6 +584,8 @@ const CreateDataset = () => { abortController.abort(); abortControllersRef.current.delete(fileUid); console.log('已取消上传:', (file as any).name); + // 取消上传后直接返回 true,允许移除文件 + return true; } // 只有当文件已经上传成功(有response.id)时,才删除服务器上的文件 @@ -586,6 +594,7 @@ const CreateDataset = () => { await deleteDocument(file.response.id); setRechunkFileIds(prev => prev.filter(id => id !== file.response.id)); console.log('已删除服务器文件:', file.response.id); + return true; } catch (error) { console.error('删除文件失败:', error); messageApi.error(t('common.deleteFailed') || '删除文件失败'); @@ -593,7 +602,7 @@ const CreateDataset = () => { } } - // 允许移除文件(无论是取消上传还是删除成功) + // 其他情况(如上传失败的文件)也允许移除 return true; }} /> )}