Merge branch 'feat/queue-based-graph-engine' into feat/rag-2

# Conflicts:
#	api/commands.py
#	api/core/app/apps/common/workflow_response_converter.py
#	api/core/llm_generator/llm_generator.py
#	api/core/plugin/entities/plugin.py
#	api/core/plugin/impl/tool.py
#	api/core/rag/index_processor/index_processor_base.py
#	api/core/workflow/entities/workflow_execution.py
#	api/core/workflow/entities/workflow_node_execution.py
#	api/core/workflow/enums.py
#	api/core/workflow/graph_engine/entities/graph.py
#	api/core/workflow/graph_engine/graph_engine.py
#	api/core/workflow/nodes/enums.py
#	api/services/dataset_service.py
This commit is contained in:
jyong
2025-08-27 16:05:59 +08:00
385 changed files with 23289 additions and 11938 deletions

View File

@ -16,7 +16,6 @@ import {
useReactFlow,
useStoreApi,
} from 'reactflow'
import { unionBy } from 'lodash-es'
import type { ToolDefaultValue } from '../block-selector/types'
import type {
Edge,
@ -238,23 +237,6 @@ export const useNodesInteractions = () => {
})
})
setEdges(newEdges)
const connectedEdges = getConnectedEdges([node], edges).filter(edge => edge.target === node.id)
const targetNodes: Node[] = []
for (let i = 0; i < connectedEdges.length; i++) {
const sourceConnectedEdges = getConnectedEdges([{ id: connectedEdges[i].source } as Node], edges).filter(edge => edge.source === connectedEdges[i].source && edge.sourceHandle === connectedEdges[i].sourceHandle)
targetNodes.push(...sourceConnectedEdges.map(edge => nodes.find(n => n.id === edge.target)!))
}
const uniqTargetNodes = unionBy(targetNodes, 'id')
if (uniqTargetNodes.length > 1) {
const newNodes = produce(nodes, (draft) => {
draft.forEach((n) => {
if (uniqTargetNodes.some(targetNode => n.id === targetNode.id))
n.data._inParallelHovering = true
})
})
setNodes(newNodes)
}
}, [store, workflowStore, getNodesReadOnly])
const handleNodeLeave = useCallback<NodeMouseHandler>((_, node) => {
@ -280,7 +262,6 @@ export const useNodesInteractions = () => {
const newNodes = produce(getNodes(), (draft) => {
draft.forEach((node) => {
node.data._isEntering = false
node.data._inParallelHovering = false
})
})
setNodes(newNodes)

View File

@ -20,7 +20,7 @@ export const useWorkflowAgentLog = () => {
if (current.execution_metadata) {
if (current.execution_metadata.agent_log) {
const currentLogIndex = current.execution_metadata.agent_log.findIndex(log => log.id === data.id)
const currentLogIndex = current.execution_metadata.agent_log.findIndex(log => log.message_id === data.message_id)
if (currentLogIndex > -1) {
current.execution_metadata.agent_log[currentLogIndex] = {
...current.execution_metadata.agent_log[currentLogIndex],

View File

@ -141,7 +141,6 @@ const BaseNode: FC<BaseNodeProps> = ({
className={cn(
'relative flex rounded-2xl border',
showSelectedBorder ? 'border-components-option-card-option-selected-border' : 'border-transparent',
!showSelectedBorder && data._inParallelHovering && 'border-workflow-block-border-highlight',
data._waitingRun && 'opacity-70',
data._dimmed && 'opacity-30',
)}
@ -174,13 +173,6 @@ const BaseNode: FC<BaseNodeProps> = ({
data._isBundled && '!shadow-lg',
)}
>
{
data._inParallelHovering && (
<div className='top system-2xs-medium-uppercase absolute -top-2.5 left-2 z-10 text-text-tertiary'>
{t('workflow.common.parallelRun')}
</div>
)
}
{
data._showAddVariablePopup && (
<AddVariablePopupWithPosition

View File

@ -466,7 +466,7 @@ export const useChat = (
if (current.execution_metadata) {
if (current.execution_metadata.agent_log) {
const currentLogIndex = current.execution_metadata.agent_log.findIndex(log => log.id === data.id)
const currentLogIndex = current.execution_metadata.agent_log.findIndex(log => log.message_id === data.message_id)
if (currentLogIndex > -1) {
current.execution_metadata.agent_log[currentLogIndex] = {
...current.execution_metadata.agent_log[currentLogIndex],

View File

@ -21,7 +21,7 @@ const AgentLogTrigger = ({
<div
className='cursor-pointer rounded-[10px] bg-components-button-tertiary-bg'
onClick={() => {
onShowAgentOrToolLog({ id: nodeInfo.id, children: agentLog || [] } as AgentLogItemWithChildren)
onShowAgentOrToolLog({ message_id: nodeInfo.id, children: agentLog || [] } as AgentLogItemWithChildren)
}}
>
<div className='system-2xs-medium-uppercase flex items-center px-3 pt-2 text-text-tertiary'>

View File

@ -16,7 +16,7 @@ const AgentResultPanel = ({
}: AgentResultPanelProps) => {
const { t } = useTranslation()
const top = agentOrToolLogItemStack[agentOrToolLogItemStack.length - 1]
const list = agentOrToolLogListMap[top.id]
const list = agentOrToolLogListMap[top.message_id]
return (
<div className='overflow-y-auto bg-background-section'>
@ -29,7 +29,7 @@ const AgentResultPanel = ({
{
list.map(item => (
<AgentLogItem
key={item.id}
key={item.message_id}
item={item}
onShowAgentOrToolLog={onShowAgentOrToolLog}
/>

View File

@ -59,9 +59,9 @@ export const useLogs = () => {
agentOrToolLogItemStackRef.current = []
return
}
const { id, children } = detail
const { message_id: id, children } = detail
let currentAgentOrToolLogItemStack = agentOrToolLogItemStackRef.current.slice()
const index = currentAgentOrToolLogItemStack.findIndex(logItem => logItem.id === id)
const index = currentAgentOrToolLogItemStack.findIndex(logItem => logItem.message_id === id)
if (index > -1)
currentAgentOrToolLogItemStack = currentAgentOrToolLogItemStack.slice(0, index + 1)

View File

@ -9,10 +9,10 @@ const remove = (node: AgentLogItemWithChildren, removeId: string) => {
if (!children || children.length === 0)
return
const hasCircle = !!children.find(c => c.id === removeId)
const hasCircle = !!children.find(c => c.message_id === removeId)
if (hasCircle) {
node.hasCircle = true
node.children = node.children.filter(c => c.id !== removeId)
node.children = node.children.filter(c => c.message_id !== removeId)
children = node.children
}
@ -28,9 +28,9 @@ const removeRepeatedSiblings = (list: AgentLogItemWithChildren[]) => {
const result: AgentLogItemWithChildren[] = []
const addedItemIds: string[] = []
list.forEach((item) => {
if (!addedItemIds.includes(item.id)) {
if (!addedItemIds.includes(item.message_id)) {
result.push(item)
addedItemIds.push(item.id)
addedItemIds.push(item.message_id)
}
})
return result
@ -39,15 +39,15 @@ const removeRepeatedSiblings = (list: AgentLogItemWithChildren[]) => {
const removeCircleLogItem = (log: AgentLogItemWithChildren) => {
const newLog = cloneDeep(log)
newLog.children = removeRepeatedSiblings(newLog.children)
let { id, children } = newLog
let { message_id: id, children } = newLog
if (!children || children.length === 0)
return log
// check one step circle
const hasOneStepCircle = !!children.find(c => c.id === id)
const hasOneStepCircle = !!children.find(c => c.message_id === id)
if (hasOneStepCircle) {
newLog.hasCircle = true
newLog.children = newLog.children.filter(c => c.id !== id)
newLog.children = newLog.children.filter(c => c.message_id !== id)
children = newLog.children
}
@ -66,7 +66,7 @@ const listToTree = (logs: AgentLogItem[]) => {
logs.forEach((log) => {
const hasParent = !!log.parent_id
if (hasParent) {
const parent = logs.find(item => item.id === log.parent_id) as AgentLogItemWithChildren
const parent = logs.find(item => item.message_id === log.parent_id) as AgentLogItemWithChildren
if (parent) {
if (!parent.children)
parent.children = []

View File

@ -11,7 +11,6 @@ import {
RiErrorWarningFill,
RiLoader2Line,
} from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import {
NodeTargetHandle,
} from '@/app/components/workflow/nodes/_base/components/node-handle'
@ -34,7 +33,6 @@ const SimpleNode: FC<SimpleNodeProps> = ({
id,
data,
}) => {
const { t } = useTranslation()
const { nodesReadOnly } = useNodesReadOnly()
const showSelectedBorder = data.selected || data._isBundled || data._isEntering
@ -57,7 +55,6 @@ const SimpleNode: FC<SimpleNodeProps> = ({
className={cn(
'flex rounded-2xl border-[2px]',
showSelectedBorder ? 'border-components-option-card-option-selected-border' : 'border-transparent',
!showSelectedBorder && data._inParallelHovering && 'border-workflow-block-border-highlight',
data._waitingRun && 'opacity-70',
)}
style={{
@ -78,13 +75,6 @@ const SimpleNode: FC<SimpleNodeProps> = ({
data._isBundled && '!shadow-lg',
)}
>
{
data._inParallelHovering && (
<div className='top system-2xs-medium-uppercase absolute -top-2.5 left-2 z-10 text-text-tertiary'>
{t('workflow.common.parallelRun')}
</div>
)
}
{
!data._isCandidate && (
<NodeTargetHandle

View File

@ -81,7 +81,6 @@ export type CommonNodeType<T = {}> = {
_holdAddVariablePopup?: boolean
_iterationLength?: number
_iterationIndex?: number
_inParallelHovering?: boolean
_waitingRun?: boolean
_retryIndex?: number
_dataSourceStartToAdd?: boolean

View File

@ -86,7 +86,6 @@ const translation = {
limit: 'Die Parallelität ist auf {{num}} Zweige beschränkt.',
depthLimit: 'Begrenzung der parallelen Verschachtelungsschicht von {{num}} Schichten',
},
parallelRun: 'Paralleler Lauf',
disconnect: 'Trennen',
jumpToNode: 'Zu diesem Knoten springen',
addParallelNode: 'Parallelen Knoten hinzufügen',

View File

@ -94,7 +94,6 @@ const translation = {
importWarning: 'Caution',
importWarningDetails: 'DSL version difference may affect certain features',
importSuccess: 'Import Successfully',
parallelRun: 'Parallel Run',
parallelTip: {
click: {
title: 'Click',

View File

@ -86,7 +86,6 @@ const translation = {
limit: 'El paralelismo se limita a {{num}} ramas.',
depthLimit: 'Límite de capa de anidamiento paralelo de capas {{num}}',
},
parallelRun: 'Ejecución paralela',
disconnect: 'Desconectar',
jumpToNode: 'Saltar a este nodo',
addParallelNode: 'Agregar nodo paralelo',

View File

@ -88,7 +88,6 @@ const translation = {
},
disconnect: 'قطع',
jumpToNode: 'پرش به این گره',
parallelRun: 'اجرای موازی',
addParallelNode: 'افزودن گره موازی',
parallel: 'موازی',
branch: 'شاخه',

View File

@ -86,7 +86,6 @@ const translation = {
limit: 'Le parallélisme est limité aux branches {{num}}.',
depthLimit: 'Limite de couches dimbrication parallèle de {{num}} couches',
},
parallelRun: 'Exécution parallèle',
disconnect: 'Déconnecter',
jumpToNode: 'Aller à ce nœud',
addParallelNode: 'Ajouter un nœud parallèle',

View File

@ -90,7 +90,6 @@ const translation = {
depthLimit: '{{num}} परतों की समानांतर नेस्टिंग परत सीमा',
},
disconnect: 'अलग करना',
parallelRun: 'समानांतर रन',
jumpToNode: 'इस नोड पर जाएं',
addParallelNode: 'समानांतर नोड जोड़ें',
parallel: 'समानांतर',

View File

@ -90,7 +90,6 @@ const translation = {
depthLimit: 'Limite di livelli di annidamento parallelo di {{num}} livelli',
limit: 'Il parallelismo è limitato ai rami {{num}}.',
},
parallelRun: 'Corsa parallela',
disconnect: 'Disconnettere',
jumpToNode: 'Vai a questo nodo',
addParallelNode: 'Aggiungi nodo parallelo',

View File

@ -93,7 +93,6 @@ const translation = {
importWarning: '注意事項',
importWarningDetails: 'DSL バージョンの違いにより機能に影響が出る可能性があります',
importSuccess: 'インポート成功',
parallelRun: '並列実行',
parallelTip: {
click: {
title: 'クリック',

View File

@ -88,7 +88,6 @@ const translation = {
depthLimit: '평행 중첩 레이어 {{num}}개 레이어의 제한',
limit: '병렬 처리는 {{num}}개의 분기로 제한됩니다.',
},
parallelRun: '병렬 실행',
disconnect: '분리하다',
jumpToNode: '이 노드로 이동',
addParallelNode: '병렬 노드 추가',

View File

@ -86,7 +86,6 @@ const translation = {
limit: 'Równoległość jest ograniczona do gałęzi {{num}}.',
depthLimit: 'Limit warstw zagnieżdżania równoległego dla warstw {{num}}',
},
parallelRun: 'Bieg równoległy',
jumpToNode: 'Przejdź do tego węzła',
disconnect: 'Odłączyć',
addParallelNode: 'Dodaj węzeł równoległy',

View File

@ -86,7 +86,6 @@ const translation = {
limit: 'O paralelismo é limitado a {{num}} ramificações.',
depthLimit: 'Limite de camada de aninhamento paralelo de {{num}} camadas',
},
parallelRun: 'Execução paralela',
disconnect: 'Desligar',
jumpToNode: 'Ir para este nó',
addParallelNode: 'Adicionar nó paralelo',

View File

@ -86,7 +86,6 @@ const translation = {
depthLimit: 'Limita straturilor de imbricare paralelă a {{num}} straturi',
limit: 'Paralelismul este limitat la {{num}} ramuri.',
},
parallelRun: 'Rulare paralelă',
disconnect: 'Deconecta',
jumpToNode: 'Sari la acest nod',
addParallelNode: 'Adăugare nod paralel',

View File

@ -86,7 +86,6 @@ const translation = {
limit: 'Параллелизм ограничен ветвями {{num}}.',
depthLimit: 'Ограничение на количество слоев параллельной вложенности {{num}}',
},
parallelRun: 'Параллельный прогон',
disconnect: 'Разъединять',
jumpToNode: 'Перейти к этому узлу',
addParallelNode: 'Добавить параллельный узел',

View File

@ -79,7 +79,6 @@ const translation = {
overwriteAndImport: 'Prepiši in uvozi',
features: 'Značilnosti',
exportPNG: 'Izvozi kot PNG',
parallelRun: 'Paralelni tek',
chooseDSL: 'Izberi DSL datoteko',
unpublished: 'Nepublikirano',
pasteHere: 'Prilepite tukaj',

View File

@ -80,7 +80,6 @@ const translation = {
importWarning: 'ความระมัดระวัง',
importWarningDetails: 'ความแตกต่างของเวอร์ชัน DSL อาจส่งผลต่อคุณสมบัติบางอย่าง',
importSuccess: 'นําเข้าสําเร็จ',
parallelRun: 'วิ่งแบบขนาน',
parallelTip: {
click: {
title: 'คลิก',

View File

@ -89,7 +89,6 @@ const translation = {
jumpToNode: 'Bu düğüme atla',
addParallelNode: 'Paralel Düğüm Ekle',
disconnect: 'Ayırmak',
parallelRun: 'Paralel Koşu',
parallel: 'PARALEL',
branch: 'DAL',
featuresDocLink: 'Daha fazla bilgi edinin',

View File

@ -87,7 +87,6 @@ const translation = {
depthLimit: 'Обмеження рівня паралельного вкладеності шарів {{num}}',
},
disconnect: 'Відключити',
parallelRun: 'Паралельний біг',
jumpToNode: 'Перейти до цього вузла',
addParallelNode: 'Додати паралельний вузол',
parallel: 'ПАРАЛЕЛЬНИЙ',

View File

@ -86,7 +86,6 @@ const translation = {
limit: 'Song song được giới hạn trong các nhánh {{num}}.',
depthLimit: 'Giới hạn lớp lồng song song của {{num}} layer',
},
parallelRun: 'Chạy song song',
disconnect: 'Ngắt kết nối',
jumpToNode: 'Chuyển đến nút này',
addParallelNode: 'Thêm nút song song',

View File

@ -93,7 +93,6 @@ const translation = {
importWarning: '注意',
importWarningDetails: 'DSL 版本差异可能影响部分功能表现',
importSuccess: '导入成功',
parallelRun: '并行运行',
parallelTip: {
click: {
title: '点击',

View File

@ -89,7 +89,6 @@ const translation = {
limit: '並行度僅限於 {{num}} 個分支。',
depthLimit: '並行嵌套層限制為 {{num}} 個層',
},
parallelRun: '並行運行',
disconnect: '斷開',
jumpToNode: '跳轉到此節點',
addParallelNode: '添加並行節點',

View File

@ -1,6 +1,6 @@
{
"name": "dify-web",
"version": "1.7.2",
"version": "1.8.0",
"private": true,
"packageManager": "pnpm@10.15.0",
"engines": {

View File

@ -9,7 +9,7 @@ import type { MutableRefObject } from 'react'
export type AgentLogItem = {
node_execution_id: string,
id: string,
message_id: string,
node_id: string,
parent_id?: string,
label: string,