diff --git a/web/app/components/base/chat/types.ts b/web/app/components/base/chat/types.ts index db88a6ddc5..9eec1811fd 100644 --- a/web/app/components/base/chat/types.ts +++ b/web/app/components/base/chat/types.ts @@ -4,6 +4,8 @@ import type { VisionSettings, } from '@/types/app' import type { IChatItem } from '@/app/components/app/chat/type' +import type { NodeTracing } from '@/types/workflow' +import type { WorkflowRunningStatus } from '@/app/components/workflow/types' export type { VisionFile } from '@/types/app' export { TransferMethod } from '@/types/app' @@ -48,7 +50,14 @@ export type ChatConfig = Omit & { supportCitationHitInfo?: boolean } -export type ChatItem = IChatItem +export type WorkflowProcess = { + status: WorkflowRunningStatus + nodes: NodeTracing[] +} + +export type ChatItem = IChatItem & { + workflowProcess?: WorkflowProcess +} export type OnSend = (message: string, files?: VisionFile[]) => void diff --git a/web/app/components/workflow/custom-edge.tsx b/web/app/components/workflow/custom-edge.tsx index 162e77fdad..b6a0313f07 100644 --- a/web/app/components/workflow/custom-edge.tsx +++ b/web/app/components/workflow/custom-edge.tsx @@ -3,7 +3,7 @@ import { useCallback, useState, } from 'react' -import { union } from 'lodash-es' +import { intersection } from 'lodash-es' import type { EdgeProps } from 'reactflow' import { BaseEdge, @@ -49,8 +49,8 @@ const CustomEdge = ({ const [open, setOpen] = useState(false) const { handleNodeAdd } = useNodesInteractions() const nodesExtraData = useNodesExtraData() - const availablePrevNodes = nodesExtraData[(data as Edge['data'])?.targetType]?.availablePrevNodes || [] - const availableNextNodes = nodesExtraData[(data as Edge['data'])?.sourceType]?.availableNextNodes || [] + const availablePrevNodes = nodesExtraData[(data as Edge['data'])!.targetType]?.availablePrevNodes || [] + const availableNextNodes = nodesExtraData[(data as Edge['data'])!.sourceType]?.availableNextNodes || [] const handleOpenChange = useCallback((v: boolean) => { setOpen(v) }, []) @@ -98,7 +98,7 @@ const CustomEdge = ({ onOpenChange={handleOpenChange} asChild onSelect={handleInsert} - availableBlocksTypes={union(availablePrevNodes, availableNextNodes)} + availableBlocksTypes={intersection(availablePrevNodes, availableNextNodes)} /> diff --git a/web/app/components/workflow/header/publish.tsx b/web/app/components/workflow/header/publish.tsx index 1e7e915298..83641323e5 100644 --- a/web/app/components/workflow/header/publish.tsx +++ b/web/app/components/workflow/header/publish.tsx @@ -8,7 +8,10 @@ import { useStore, useWorkflowStore, } from '../store' -import { useWorkflow } from '../hooks' +import { + useNodesSyncDraft, + useWorkflow, +} from '../hooks' import Button from '@/app/components/base/button' import { PortalToFollowElem, @@ -23,6 +26,7 @@ const Publish = () => { const [published, setPublished] = useState(false) const workflowStore = useWorkflowStore() const { formatTimeFromNow } = useWorkflow() + const { handleSyncWorkflowDraft } = useNodesSyncDraft() const runningStatus = useStore(s => s.runningStatus) const draftUpdatedAt = useStore(s => s.draftUpdatedAt) const publishedAt = useStore(s => s.publishedAt) @@ -56,10 +60,11 @@ const Publish = () => { setOpen(false) if (!open) { + handleSyncWorkflowDraft(true) setOpen(true) setPublished(false) } - }, [runningStatus, open]) + }, [runningStatus, open, handleSyncWorkflowDraft]) return ( { targetType: newNode.data.type, }, } - const newNextEdge = { - id: `${newNode.id}-${nextNodeId}`, - type: 'custom', - source: newNode.id, - sourceHandle, - target: nextNodeId, - targetHandle: nextNodeTargetHandle, - data: { - sourceType: newNode.data.type, - targetType: nextNode.data.type, - }, + let newNextEdge: Edge | null = null + if (nodeType !== BlockEnum.IfElse && nodeType !== BlockEnum.QuestionClassifier) { + newNextEdge = { + id: `${newNode.id}-${nextNodeId}`, + type: 'custom', + source: newNode.id, + sourceHandle, + target: nextNodeId, + targetHandle: nextNodeTargetHandle, + data: { + sourceType: newNode.data.type, + targetType: nextNode.data.type, + }, + } } const nodesConnectedSourceOrTargetHandleIdsMap = getNodesConnectedSourceOrTargetHandleIdsMap( [ { type: 'remove', edge: edges[currentEdgeIndex] }, { type: 'add', edge: newPrevEdge }, - { type: 'add', edge: newNextEdge }, + ...(newNextEdge ? [{ type: 'add', edge: newNextEdge }] : []), ], nodes, ) @@ -582,7 +586,9 @@ export const useNodesInteractions = () => { const newEdges = produce(edges, (draft) => { draft.splice(currentEdgeIndex, 1) draft.push(newPrevEdge) - draft.push(newNextEdge) + + if (newNextEdge) + draft.push(newNextEdge) }) setEdges(newEdges) } diff --git a/web/app/components/workflow/nodes/_base/components/next-step/item.tsx b/web/app/components/workflow/nodes/_base/components/next-step/item.tsx index 8cd206e734..e2d2f9e67e 100644 --- a/web/app/components/workflow/nodes/_base/components/next-step/item.tsx +++ b/web/app/components/workflow/nodes/_base/components/next-step/item.tsx @@ -4,7 +4,7 @@ import { useMemo, } from 'react' import { useTranslation } from 'react-i18next' -import { union } from 'lodash-es' +import { intersection } from 'lodash-es' import type { CommonNodeType, OnSelectBlock, @@ -86,7 +86,7 @@ const Item = ({ }} trigger={renderTrigger} popupClassName='!w-[328px]' - availableBlocksTypes={union(availablePrevNodes, availableNextNodes)} + availableBlocksTypes={intersection(availablePrevNodes, availableNextNodes)} /> ) diff --git a/web/app/components/workflow/nodes/_base/components/panel-operator/change-block.tsx b/web/app/components/workflow/nodes/_base/components/panel-operator/change-block.tsx index 21d1b4d776..883c8eabbf 100644 --- a/web/app/components/workflow/nodes/_base/components/panel-operator/change-block.tsx +++ b/web/app/components/workflow/nodes/_base/components/panel-operator/change-block.tsx @@ -3,7 +3,7 @@ import { useCallback, } from 'react' import { useTranslation } from 'react-i18next' -import { union } from 'lodash-es' +import { intersection } from 'lodash-es' import BlockSelector from '@/app/components/workflow/block-selector' import { useNodesExtraData, @@ -51,7 +51,7 @@ const ChangeBlock = ({ }} onSelect={handleSelect} trigger={renderTrigger} - availableBlocksTypes={union(availablePrevNodes, availableNextNodes)} + availableBlocksTypes={intersection(availablePrevNodes, availableNextNodes)} /> ) } diff --git a/web/app/components/workflow/nodes/answer/default.ts b/web/app/components/workflow/nodes/answer/default.ts index 8bd2a5dff0..fba4a491ef 100644 --- a/web/app/components/workflow/nodes/answer/default.ts +++ b/web/app/components/workflow/nodes/answer/default.ts @@ -1,4 +1,5 @@ -import { BlockEnum, type NodeDefault } from '../../types' +import { BlockEnum } from '../../types' +import type { NodeDefault } from '../../types' import type { AnswerNodeType } from './types' import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/constants' @@ -9,12 +10,15 @@ const nodeDefault: NodeDefault = { }, getAvailablePrevNodes(isChatMode: boolean) { const nodes = isChatMode - ? ALL_CHAT_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.Answer) + ? ALL_CHAT_AVAILABLE_BLOCKS : ALL_COMPLETION_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.End) return nodes }, - getAvailableNextNodes() { - return [] + getAvailableNextNodes(isChatMode: boolean) { + const nodes = isChatMode + ? ALL_CHAT_AVAILABLE_BLOCKS + : ALL_COMPLETION_AVAILABLE_BLOCKS + return nodes }, checkValid(payload: AnswerNodeType) { let isValid = true diff --git a/web/app/components/workflow/nodes/code/default.ts b/web/app/components/workflow/nodes/code/default.ts index d350055c0c..fa9b9398a4 100644 --- a/web/app/components/workflow/nodes/code/default.ts +++ b/web/app/components/workflow/nodes/code/default.ts @@ -14,7 +14,7 @@ const nodeDefault: NodeDefault = { }, getAvailablePrevNodes(isChatMode: boolean) { const nodes = isChatMode - ? ALL_CHAT_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.Answer) + ? ALL_CHAT_AVAILABLE_BLOCKS : ALL_COMPLETION_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.End) return nodes }, diff --git a/web/app/components/workflow/nodes/end/default.ts b/web/app/components/workflow/nodes/end/default.ts index b99bde971b..ceeda5b43b 100644 --- a/web/app/components/workflow/nodes/end/default.ts +++ b/web/app/components/workflow/nodes/end/default.ts @@ -9,7 +9,7 @@ const nodeDefault: NodeDefault = { }, getAvailablePrevNodes(isChatMode: boolean) { const nodes = isChatMode - ? ALL_CHAT_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.Answer) + ? ALL_CHAT_AVAILABLE_BLOCKS : ALL_COMPLETION_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.End) return nodes }, diff --git a/web/app/components/workflow/nodes/http/default.ts b/web/app/components/workflow/nodes/http/default.ts index 0587d88461..2e26712f08 100644 --- a/web/app/components/workflow/nodes/http/default.ts +++ b/web/app/components/workflow/nodes/http/default.ts @@ -21,7 +21,7 @@ const nodeDefault: NodeDefault = { }, getAvailablePrevNodes(isChatMode: boolean) { const nodes = isChatMode - ? ALL_CHAT_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.Answer) + ? ALL_CHAT_AVAILABLE_BLOCKS : ALL_COMPLETION_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.End) return nodes }, diff --git a/web/app/components/workflow/nodes/if-else/default.ts b/web/app/components/workflow/nodes/if-else/default.ts index 8c4c9f1b8e..7a74ddbdf6 100644 --- a/web/app/components/workflow/nodes/if-else/default.ts +++ b/web/app/components/workflow/nodes/if-else/default.ts @@ -19,7 +19,7 @@ const nodeDefault: NodeDefault = { }, getAvailablePrevNodes(isChatMode: boolean) { const nodes = isChatMode - ? ALL_CHAT_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.Answer) + ? ALL_CHAT_AVAILABLE_BLOCKS : ALL_COMPLETION_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.End) return nodes }, diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/default.ts b/web/app/components/workflow/nodes/knowledge-retrieval/default.ts index 540cb8a869..22edbc2360 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/default.ts +++ b/web/app/components/workflow/nodes/knowledge-retrieval/default.ts @@ -14,7 +14,7 @@ const nodeDefault: NodeDefault = { }, getAvailablePrevNodes(isChatMode: boolean) { const nodes = isChatMode - ? ALL_CHAT_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.Answer) + ? ALL_CHAT_AVAILABLE_BLOCKS : ALL_COMPLETION_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.End) return nodes }, diff --git a/web/app/components/workflow/nodes/llm/default.ts b/web/app/components/workflow/nodes/llm/default.ts index 9ec45824db..4f0b79ed1e 100644 --- a/web/app/components/workflow/nodes/llm/default.ts +++ b/web/app/components/workflow/nodes/llm/default.ts @@ -37,7 +37,7 @@ const nodeDefault: NodeDefault = { }, getAvailablePrevNodes(isChatMode: boolean) { const nodes = isChatMode - ? ALL_CHAT_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.Answer) + ? ALL_CHAT_AVAILABLE_BLOCKS : ALL_COMPLETION_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.End) return nodes }, diff --git a/web/app/components/workflow/nodes/question-classifier/default.ts b/web/app/components/workflow/nodes/question-classifier/default.ts index f557ab22f0..a8af8be940 100644 --- a/web/app/components/workflow/nodes/question-classifier/default.ts +++ b/web/app/components/workflow/nodes/question-classifier/default.ts @@ -18,7 +18,7 @@ const nodeDefault: NodeDefault = { }, getAvailablePrevNodes(isChatMode: boolean) { const nodes = isChatMode - ? ALL_CHAT_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.Answer) + ? ALL_CHAT_AVAILABLE_BLOCKS : ALL_COMPLETION_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.End) return nodes }, diff --git a/web/app/components/workflow/nodes/start/default.ts b/web/app/components/workflow/nodes/start/default.ts index e443a1b1dd..a3c7ae1560 100644 --- a/web/app/components/workflow/nodes/start/default.ts +++ b/web/app/components/workflow/nodes/start/default.ts @@ -6,9 +6,8 @@ const nodeDefault: NodeDefault = { defaultValue: { variables: [], }, - getAvailablePrevNodes(isChatMode: boolean) { - const nodes = isChatMode ? ALL_CHAT_AVAILABLE_BLOCKS : ALL_COMPLETION_AVAILABLE_BLOCKS - return nodes + getAvailablePrevNodes() { + return [] }, getAvailableNextNodes(isChatMode: boolean) { const nodes = isChatMode ? ALL_CHAT_AVAILABLE_BLOCKS : ALL_COMPLETION_AVAILABLE_BLOCKS diff --git a/web/app/components/workflow/nodes/template-transform/default.ts b/web/app/components/workflow/nodes/template-transform/default.ts index 333d6f85e8..aff5ae8e96 100644 --- a/web/app/components/workflow/nodes/template-transform/default.ts +++ b/web/app/components/workflow/nodes/template-transform/default.ts @@ -9,7 +9,7 @@ const nodeDefault: NodeDefault = { }, getAvailablePrevNodes(isChatMode: boolean) { const nodes = isChatMode - ? ALL_CHAT_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.Answer) + ? ALL_CHAT_AVAILABLE_BLOCKS : ALL_COMPLETION_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.End) return nodes }, diff --git a/web/app/components/workflow/nodes/tool/default.ts b/web/app/components/workflow/nodes/tool/default.ts index 2c4d1f249b..24d1dc29a7 100644 --- a/web/app/components/workflow/nodes/tool/default.ts +++ b/web/app/components/workflow/nodes/tool/default.ts @@ -10,7 +10,7 @@ const nodeDefault: NodeDefault = { }, getAvailablePrevNodes(isChatMode: boolean) { const nodes = isChatMode - ? ALL_CHAT_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.Answer) + ? ALL_CHAT_AVAILABLE_BLOCKS : ALL_COMPLETION_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.End) return nodes }, diff --git a/web/app/components/workflow/nodes/variable-assigner/default.ts b/web/app/components/workflow/nodes/variable-assigner/default.ts index 8ca5bea7cb..15793b0952 100644 --- a/web/app/components/workflow/nodes/variable-assigner/default.ts +++ b/web/app/components/workflow/nodes/variable-assigner/default.ts @@ -10,7 +10,7 @@ const nodeDefault: NodeDefault = { }, getAvailablePrevNodes(isChatMode: boolean) { const nodes = isChatMode - ? ALL_CHAT_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.Answer) + ? ALL_CHAT_AVAILABLE_BLOCKS : ALL_COMPLETION_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.End) return nodes.filter(type => type !== BlockEnum.IfElse && type !== BlockEnum.QuestionClassifier) }, diff --git a/web/app/components/workflow/panel/debug-and-preview/hooks.ts b/web/app/components/workflow/panel/debug-and-preview/hooks.ts index 4e1a2ea121..023b4be6b3 100644 --- a/web/app/components/workflow/panel/debug-and-preview/hooks.ts +++ b/web/app/components/workflow/panel/debug-and-preview/hooks.ts @@ -224,6 +224,10 @@ export const useChat = ( }) handleUpdateChatList(newChatList) }, + onWorkflowStarted: () => {}, + onWorkflowFinished: () => {}, + onNodeStarted: () => {}, + onNodeFinished: () => {}, }, ) }, [handleRun, handleResponding, handleUpdateChatList, notify, t, updateCurrentQA, config.suggested_questions_after_answer?.enabled])