dify/web/app/components/workflow/panel/debug-and-preview/hooks/use-chat.ts
yyh 30b9295156
Merge remote-tracking branch 'origin/build/feat/hitl' into wip/hitl-merge-web-conflicts-20260206-175434
# Conflicts:
#	api/.env.example
#	api/core/app/apps/advanced_chat/app_generator.py
#	api/core/app/apps/advanced_chat/app_runner.py
#	api/core/app/apps/advanced_chat/generate_task_pipeline.py
#	api/core/app/apps/workflow/app_generator.py
#	api/core/app/apps/workflow/app_runner.py
#	api/core/app/entities/queue_entities.py
#	api/core/workflow/node_events/__init__.py
#	api/core/workflow/runtime/graph_runtime_state.py
#	api/fields/message_fields.py
#	api/services/workflow_service.py
#	web/app/components/app/app-publisher/index.tsx
#	web/app/components/base/chat/chat/answer/index.tsx
#	web/app/components/base/chat/chat/hooks.ts
#	web/app/components/base/chat/chat/type.ts
#	web/app/components/base/prompt-editor/index.tsx
#	web/app/components/rag-pipeline/hooks/use-available-nodes-meta-data.ts
#	web/app/components/share/text-generation/result/header.tsx
#	web/app/components/workflow-app/components/workflow-header/features-trigger.tsx
#	web/app/components/workflow-app/hooks/use-workflow-run.ts
#	web/app/components/workflow/hooks/use-checklist.ts
#	web/app/components/workflow/hooks/use-fetch-workflow-inspect-vars.ts
#	web/app/components/workflow/hooks/use-workflow.ts
#	web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx
#	web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx
#	web/app/components/workflow/nodes/_base/node.tsx
#	web/app/components/workflow/nodes/tool/components/mixed-variable-text-input/components/placeholder.tsx
#	web/app/components/workflow/panel/debug-and-preview/hooks.ts
#	web/app/components/workflow/panel/workflow-preview.tsx
#	web/app/components/workflow/store/workflow/workflow-slice.ts
#	web/eslint-suppressions.json
#	web/i18n/en-US/common.json
#	web/i18n/zh-Hans/common.json
#	web/i18n/zh-Hant/common.json
#	web/service/workflow.ts
2026-02-06 19:13:51 +08:00

120 lines
3.4 KiB
TypeScript

import type { ChatConfig, SendCallback } from './types'
import type { InputForm } from '@/app/components/base/chat/chat/type'
import type { ChatItemInTree, Inputs } from '@/app/components/base/chat/types'
import { useCallback, useEffect, useRef } from 'react'
import { useStoreApi } from 'reactflow'
import { CUSTOM_NODE } from '../../../constants'
import { useStore } from '../../../store'
import { useChatFlowControl } from './use-chat-flow-control'
import { useChatList } from './use-chat-list'
import { useChatMessageSender } from './use-chat-message-sender'
import { useChatTreeOperations } from './use-chat-tree-operations'
export function useChat(
config: ChatConfig | undefined,
formSettings?: {
inputs: Inputs
inputsForm: InputForm[]
},
prevChatTree?: ChatItemInTree[],
stopChat?: (taskId: string) => void,
) {
const chatTree = useStore(s => s.chatTree)
const conversationId = useStore(s => s.conversationId)
const isResponding = useStore(s => s.isResponding)
const suggestedQuestions = useStore(s => s.suggestedQuestions)
const targetMessageId = useStore(s => s.targetMessageId)
const updateChatTree = useStore(s => s.updateChatTree)
const setTargetMessageId = useStore(s => s.setTargetMessageId)
const store = useStoreApi()
const initialChatTreeRef = useRef(prevChatTree)
useEffect(() => {
const initialChatTree = initialChatTreeRef.current
if (!initialChatTree || initialChatTree.length === 0)
return
updateChatTree(currentChatTree => (currentChatTree.length === 0 ? initialChatTree : currentChatTree))
}, [updateChatTree])
const { updateCurrentQAOnTree } = useChatTreeOperations(updateChatTree)
const {
handleResponding,
handleStop,
handleRestart,
} = useChatFlowControl({
stopChat,
})
const {
threadMessages,
chatList,
} = useChatList({
chatTree,
targetMessageId,
config,
formSettings,
})
const {
handleSend,
handleResume,
handleSubmitHumanInputForm,
} = useChatMessageSender({
threadMessages,
config,
formSettings,
handleResponding,
updateCurrentQAOnTree,
})
const getHumanInputNodeData = useCallback((nodeID: string) => {
const { getNodes } = store.getState()
const nodes = getNodes().filter(node => node.type === CUSTOM_NODE)
return nodes.find(node => node.id === nodeID)
}, [store])
const handleSwitchSibling = useCallback((
siblingMessageId: string,
callbacks: SendCallback,
) => {
setTargetMessageId(siblingMessageId)
const findMessageInTree = (nodes: ChatItemInTree[], targetId: string): ChatItemInTree | undefined => {
for (const node of nodes) {
if (node.id === targetId)
return node
if (node.children) {
const found = findMessageInTree(node.children, targetId)
if (found)
return found
}
}
return undefined
}
const targetMessage = findMessageInTree(chatTree, siblingMessageId)
if (targetMessage?.workflow_run_id && targetMessage.humanInputFormDataList?.length) {
handleResume(
targetMessage.id,
targetMessage.workflow_run_id,
callbacks,
)
}
}, [chatTree, handleResume, setTargetMessageId])
return {
conversationId,
chatList,
setTargetMessageId,
handleSend,
handleSwitchSibling,
handleSubmitHumanInputForm,
getHumanInputNodeData,
handleStop,
handleRestart,
isResponding,
suggestedQuestions,
}
}