mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-19 20:59:09 +08:00
fix: chat check inputs form
This commit is contained in:
parent
9ce9a52a86
commit
1d027fa065
@ -55,7 +55,7 @@ const ChatWrapper = () => {
|
|||||||
appConfig,
|
appConfig,
|
||||||
{
|
{
|
||||||
inputs: (currentConversationId ? currentConversationItem?.inputs : newConversationInputs) as any,
|
inputs: (currentConversationId ? currentConversationItem?.inputs : newConversationInputs) as any,
|
||||||
promptVariables: inputsForms,
|
inputsForm: inputsForms,
|
||||||
},
|
},
|
||||||
appPrevChatList,
|
appPrevChatList,
|
||||||
taskId => stopChatMessageResponding('', taskId, isInstalledApp, appId),
|
taskId => stopChatMessageResponding('', taskId, isInstalledApp, appId),
|
||||||
@ -165,6 +165,8 @@ const ChatWrapper = () => {
|
|||||||
chatFooterClassName='pb-4'
|
chatFooterClassName='pb-4'
|
||||||
chatFooterInnerClassName={`mx-auto w-full max-w-[720px] ${isMobile && 'px-4'}`}
|
chatFooterInnerClassName={`mx-auto w-full max-w-[720px] ${isMobile && 'px-4'}`}
|
||||||
onSend={doSend}
|
onSend={doSend}
|
||||||
|
inputs={currentConversationId ? currentConversationItem?.inputs as any : newConversationInputs}
|
||||||
|
inputsForm={inputsForms}
|
||||||
onRegenerate={doRegenerate}
|
onRegenerate={doRegenerate}
|
||||||
onStopResponding={handleStop}
|
onStopResponding={handleStop}
|
||||||
chatNode={chatNode}
|
chatNode={chatNode}
|
||||||
|
@ -11,6 +11,8 @@ import type {
|
|||||||
OnSend,
|
OnSend,
|
||||||
} from '../../types'
|
} from '../../types'
|
||||||
import type { Theme } from '../../embedded-chatbot/theme/theme-context'
|
import type { Theme } from '../../embedded-chatbot/theme/theme-context'
|
||||||
|
import type { InputForm } from '../type'
|
||||||
|
import { useCheckInputsForms } from '../check-input-forms-hooks'
|
||||||
import { useTextAreaHeight } from './hooks'
|
import { useTextAreaHeight } from './hooks'
|
||||||
import Operation from './operation'
|
import Operation from './operation'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
@ -18,7 +20,7 @@ import { FileListInChatInput } from '@/app/components/base/file-uploader'
|
|||||||
import { useFile } from '@/app/components/base/file-uploader/hooks'
|
import { useFile } from '@/app/components/base/file-uploader/hooks'
|
||||||
import {
|
import {
|
||||||
FileContextProvider,
|
FileContextProvider,
|
||||||
useStore,
|
useFileStore,
|
||||||
} from '@/app/components/base/file-uploader/store'
|
} from '@/app/components/base/file-uploader/store'
|
||||||
import VoiceInput from '@/app/components/base/voice-input'
|
import VoiceInput from '@/app/components/base/voice-input'
|
||||||
import { useToastContext } from '@/app/components/base/toast'
|
import { useToastContext } from '@/app/components/base/toast'
|
||||||
@ -34,7 +36,8 @@ type ChatInputAreaProps = {
|
|||||||
visionConfig?: FileUpload
|
visionConfig?: FileUpload
|
||||||
speechToTextConfig?: EnableType
|
speechToTextConfig?: EnableType
|
||||||
onSend?: OnSend
|
onSend?: OnSend
|
||||||
onSendCheck?: () => boolean
|
inputs?: Record<string, any>
|
||||||
|
inputsForm?: InputForm[]
|
||||||
theme?: Theme | null
|
theme?: Theme | null
|
||||||
}
|
}
|
||||||
const ChatInputArea = ({
|
const ChatInputArea = ({
|
||||||
@ -45,7 +48,8 @@ const ChatInputArea = ({
|
|||||||
visionConfig,
|
visionConfig,
|
||||||
speechToTextConfig = { enabled: true },
|
speechToTextConfig = { enabled: true },
|
||||||
onSend,
|
onSend,
|
||||||
onSendCheck = () => true,
|
inputs = {},
|
||||||
|
inputsForm = [],
|
||||||
// theme,
|
// theme,
|
||||||
}: ChatInputAreaProps) => {
|
}: ChatInputAreaProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@ -61,8 +65,7 @@ const ChatInputArea = ({
|
|||||||
const [query, setQuery] = useState('')
|
const [query, setQuery] = useState('')
|
||||||
const isUseInputMethod = useRef(false)
|
const isUseInputMethod = useRef(false)
|
||||||
const [showVoiceInput, setShowVoiceInput] = useState(false)
|
const [showVoiceInput, setShowVoiceInput] = useState(false)
|
||||||
const files = useStore(s => s.files)
|
const filesStore = useFileStore()
|
||||||
const setFiles = useStore(s => s.setFiles)
|
|
||||||
const {
|
const {
|
||||||
handleDragFileEnter,
|
handleDragFileEnter,
|
||||||
handleDragFileLeave,
|
handleDragFileLeave,
|
||||||
@ -71,9 +74,11 @@ const ChatInputArea = ({
|
|||||||
handleClipboardPasteFile,
|
handleClipboardPasteFile,
|
||||||
isDragActive,
|
isDragActive,
|
||||||
} = useFile(visionConfig!)
|
} = useFile(visionConfig!)
|
||||||
|
const { checkInputsForm } = useCheckInputsForms()
|
||||||
|
|
||||||
const handleSend = () => {
|
const handleSend = () => {
|
||||||
if (onSend) {
|
if (onSend) {
|
||||||
|
const { files, setFiles } = filesStore.getState()
|
||||||
if (files.find(item => item.transferMethod === TransferMethod.local_file && !item.uploadedId)) {
|
if (files.find(item => item.transferMethod === TransferMethod.local_file && !item.uploadedId)) {
|
||||||
notify({ type: 'info', message: t('appDebug.errorMessage.waitForImgUpload') })
|
notify({ type: 'info', message: t('appDebug.errorMessage.waitForImgUpload') })
|
||||||
return
|
return
|
||||||
@ -82,7 +87,7 @@ const ChatInputArea = ({
|
|||||||
notify({ type: 'info', message: t('appAnnotation.errorMessage.queryRequired') })
|
notify({ type: 'info', message: t('appAnnotation.errorMessage.queryRequired') })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (onSendCheck()) {
|
if (checkInputsForm(inputs, inputsForm)) {
|
||||||
onSend(query, files)
|
onSend(query, files)
|
||||||
setQuery('')
|
setQuery('')
|
||||||
setFiles([])
|
setFiles([])
|
||||||
|
51
web/app/components/base/chat/chat/check-input-forms-hooks.ts
Normal file
51
web/app/components/base/chat/chat/check-input-forms-hooks.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { useCallback } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import type { InputForm } from './type'
|
||||||
|
import { useToastContext } from '@/app/components/base/toast'
|
||||||
|
import { InputVarType } from '@/app/components/workflow/types'
|
||||||
|
import { TransferMethod } from '@/types/app'
|
||||||
|
|
||||||
|
export const useCheckInputsForms = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const { notify } = useToastContext()
|
||||||
|
|
||||||
|
const checkInputsForm = useCallback((inputs: Record<string, any>, inputsForm: InputForm[]) => {
|
||||||
|
let hasEmptyInput = ''
|
||||||
|
let fileIsUploading = false
|
||||||
|
const requiredVars = inputsForm.filter(({ required }) => required)
|
||||||
|
|
||||||
|
if (requiredVars?.length) {
|
||||||
|
requiredVars.forEach(({ variable, label, type }) => {
|
||||||
|
if (hasEmptyInput)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (!inputs[variable])
|
||||||
|
hasEmptyInput = label as string
|
||||||
|
|
||||||
|
if ((type === InputVarType.singleFile || type === InputVarType.multiFiles) && inputs[variable]) {
|
||||||
|
const files = inputs[variable]
|
||||||
|
if (Array.isArray(files))
|
||||||
|
fileIsUploading = files.find(item => item.transferMethod === TransferMethod.local_file && !item.uploadedId)
|
||||||
|
else
|
||||||
|
fileIsUploading = files.transfer_method === TransferMethod.local_file && !files.uploadedId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasEmptyInput) {
|
||||||
|
notify({ type: 'error', message: t('appDebug.errorMessage.valueOfVarRequired', { key: hasEmptyInput }) })
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileIsUploading) {
|
||||||
|
notify({ type: 'info', message: t('appDebug.errorMessage.waitForImgUpload') })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}, [notify, t])
|
||||||
|
|
||||||
|
return {
|
||||||
|
checkInputsForm,
|
||||||
|
}
|
||||||
|
}
|
@ -12,12 +12,12 @@ import type {
|
|||||||
ChatConfig,
|
ChatConfig,
|
||||||
ChatItem,
|
ChatItem,
|
||||||
Inputs,
|
Inputs,
|
||||||
PromptVariable,
|
|
||||||
} from '../types'
|
} from '../types'
|
||||||
|
import type { InputForm } from './type'
|
||||||
|
import { processOpeningStatement } from './utils'
|
||||||
import { TransferMethod } from '@/types/app'
|
import { TransferMethod } from '@/types/app'
|
||||||
import { useToastContext } from '@/app/components/base/toast'
|
import { useToastContext } from '@/app/components/base/toast'
|
||||||
import { ssePost } from '@/service/base'
|
import { ssePost } from '@/service/base'
|
||||||
import { replaceStringWithValues } from '@/app/components/app/configuration/prompt-value-panel/utils'
|
|
||||||
import type { Annotation } from '@/models/log'
|
import type { Annotation } from '@/models/log'
|
||||||
import { WorkflowRunningStatus } from '@/app/components/workflow/types'
|
import { WorkflowRunningStatus } from '@/app/components/workflow/types'
|
||||||
import useTimestamp from '@/hooks/use-timestamp'
|
import useTimestamp from '@/hooks/use-timestamp'
|
||||||
@ -33,50 +33,11 @@ type SendCallback = {
|
|||||||
isPublicAPI?: boolean
|
isPublicAPI?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useCheckPromptVariables = () => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
const { notify } = useToastContext()
|
|
||||||
|
|
||||||
const checkPromptVariables = useCallback((promptVariablesConfig: {
|
|
||||||
inputs: Inputs
|
|
||||||
promptVariables: PromptVariable[]
|
|
||||||
}) => {
|
|
||||||
const {
|
|
||||||
promptVariables,
|
|
||||||
inputs,
|
|
||||||
} = promptVariablesConfig
|
|
||||||
let hasEmptyInput = ''
|
|
||||||
const requiredVars = promptVariables.filter(({ key, name, required, type }) => {
|
|
||||||
if (type !== 'string' && type !== 'paragraph' && type !== 'select')
|
|
||||||
return false
|
|
||||||
const res = (!key || !key.trim()) || (!name || !name.trim()) || (required || required === undefined || required === null)
|
|
||||||
return res
|
|
||||||
})
|
|
||||||
|
|
||||||
if (requiredVars?.length) {
|
|
||||||
requiredVars.forEach(({ key, name }) => {
|
|
||||||
if (hasEmptyInput)
|
|
||||||
return
|
|
||||||
|
|
||||||
if (!inputs[key])
|
|
||||||
hasEmptyInput = name
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasEmptyInput) {
|
|
||||||
notify({ type: 'error', message: t('appDebug.errorMessage.valueOfVarRequired', { key: hasEmptyInput }) })
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}, [notify, t])
|
|
||||||
|
|
||||||
return checkPromptVariables
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useChat = (
|
export const useChat = (
|
||||||
config?: ChatConfig,
|
config?: ChatConfig,
|
||||||
promptVariablesConfig?: {
|
formSettings?: {
|
||||||
inputs: Inputs
|
inputs: Inputs
|
||||||
promptVariables: PromptVariable[]
|
inputsForm: InputForm[]
|
||||||
},
|
},
|
||||||
prevChatList?: ChatItem[],
|
prevChatList?: ChatItem[],
|
||||||
stopChat?: (taskId: string) => void,
|
stopChat?: (taskId: string) => void,
|
||||||
@ -94,7 +55,6 @@ export const useChat = (
|
|||||||
const [suggestedQuestions, setSuggestQuestions] = useState<string[]>([])
|
const [suggestedQuestions, setSuggestQuestions] = useState<string[]>([])
|
||||||
const conversationMessagesAbortControllerRef = useRef<AbortController | null>(null)
|
const conversationMessagesAbortControllerRef = useRef<AbortController | null>(null)
|
||||||
const suggestedQuestionsAbortControllerRef = useRef<AbortController | null>(null)
|
const suggestedQuestionsAbortControllerRef = useRef<AbortController | null>(null)
|
||||||
const checkPromptVariables = useCheckPromptVariables()
|
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
const pathname = usePathname()
|
const pathname = usePathname()
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -114,8 +74,8 @@ export const useChat = (
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const getIntroduction = useCallback((str: string) => {
|
const getIntroduction = useCallback((str: string) => {
|
||||||
return replaceStringWithValues(str, promptVariablesConfig?.promptVariables || [], promptVariablesConfig?.inputs || {})
|
return processOpeningStatement(str, formSettings?.inputs || {}, formSettings?.inputsForm || [])
|
||||||
}, [promptVariablesConfig?.inputs, promptVariablesConfig?.promptVariables])
|
}, [formSettings?.inputs, formSettings?.inputsForm])
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (config?.opening_statement) {
|
if (config?.opening_statement) {
|
||||||
handleUpdateChatList(produce(chatListRef.current, (draft) => {
|
handleUpdateChatList(produce(chatListRef.current, (draft) => {
|
||||||
@ -216,9 +176,6 @@ export const useChat = (
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (promptVariablesConfig?.inputs && promptVariablesConfig?.promptVariables)
|
|
||||||
checkPromptVariables(promptVariablesConfig)
|
|
||||||
|
|
||||||
const questionId = `question-${Date.now()}`
|
const questionId = `question-${Date.now()}`
|
||||||
const questionItem = {
|
const questionItem = {
|
||||||
id: questionId,
|
id: questionId,
|
||||||
@ -575,15 +532,16 @@ export const useChat = (
|
|||||||
})
|
})
|
||||||
return true
|
return true
|
||||||
}, [
|
}, [
|
||||||
checkPromptVariables,
|
|
||||||
config?.suggested_questions_after_answer,
|
config?.suggested_questions_after_answer,
|
||||||
updateCurrentQA,
|
updateCurrentQA,
|
||||||
t,
|
t,
|
||||||
notify,
|
notify,
|
||||||
promptVariablesConfig,
|
|
||||||
handleUpdateChatList,
|
handleUpdateChatList,
|
||||||
handleResponding,
|
handleResponding,
|
||||||
formatTime,
|
formatTime,
|
||||||
|
params.token,
|
||||||
|
params.appId,
|
||||||
|
pathname,
|
||||||
])
|
])
|
||||||
|
|
||||||
const handleAnnotationEdited = useCallback((query: string, answer: string, index: number) => {
|
const handleAnnotationEdited = useCallback((query: string, answer: string, index: number) => {
|
||||||
|
@ -25,6 +25,7 @@ import Answer from './answer'
|
|||||||
import ChatInputArea from './chat-input-area'
|
import ChatInputArea from './chat-input-area'
|
||||||
import TryToAsk from './try-to-ask'
|
import TryToAsk from './try-to-ask'
|
||||||
import { ChatContextProvider } from './context'
|
import { ChatContextProvider } from './context'
|
||||||
|
import type { InputForm } from './type'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import type { Emoji } from '@/app/components/tools/types'
|
import type { Emoji } from '@/app/components/tools/types'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
@ -43,7 +44,8 @@ export type ChatProps = {
|
|||||||
onStopResponding?: () => void
|
onStopResponding?: () => void
|
||||||
noChatInput?: boolean
|
noChatInput?: boolean
|
||||||
onSend?: OnSend
|
onSend?: OnSend
|
||||||
onSendCheck?: () => boolean
|
inputs?: Record<string, any>
|
||||||
|
inputsForm?: InputForm[]
|
||||||
onRegenerate?: OnRegenerate
|
onRegenerate?: OnRegenerate
|
||||||
chatContainerClassName?: string
|
chatContainerClassName?: string
|
||||||
chatContainerInnerClassName?: string
|
chatContainerInnerClassName?: string
|
||||||
@ -73,7 +75,8 @@ const Chat: FC<ChatProps> = ({
|
|||||||
appData,
|
appData,
|
||||||
config,
|
config,
|
||||||
onSend,
|
onSend,
|
||||||
onSendCheck,
|
inputs,
|
||||||
|
inputsForm,
|
||||||
onRegenerate,
|
onRegenerate,
|
||||||
chatList,
|
chatList,
|
||||||
isResponding,
|
isResponding,
|
||||||
@ -283,7 +286,8 @@ const Chat: FC<ChatProps> = ({
|
|||||||
visionConfig={config?.file_upload}
|
visionConfig={config?.file_upload}
|
||||||
speechToTextConfig={config?.speech_to_text}
|
speechToTextConfig={config?.speech_to_text}
|
||||||
onSend={onSend}
|
onSend={onSend}
|
||||||
onSendCheck={onSendCheck}
|
inputs={inputs}
|
||||||
|
inputsForm={inputsForm}
|
||||||
theme={themeBuilder?.theme}
|
theme={themeBuilder?.theme}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
@ -2,6 +2,7 @@ import type { TypeWithI18N } from '@/app/components/header/account-setting/model
|
|||||||
import type { Annotation, MessageRating } from '@/models/log'
|
import type { Annotation, MessageRating } from '@/models/log'
|
||||||
import type { VisionFile } from '@/types/app'
|
import type { VisionFile } from '@/types/app'
|
||||||
import type { FileEntity } from '@/app/components/base/file-uploader/types'
|
import type { FileEntity } from '@/app/components/base/file-uploader/types'
|
||||||
|
import type { InputVarType } from '@/app/components/workflow/types'
|
||||||
|
|
||||||
export type MessageMore = {
|
export type MessageMore = {
|
||||||
time: string
|
time: string
|
||||||
@ -130,3 +131,11 @@ export type AnnotationReply = {
|
|||||||
annotation_id: string
|
annotation_id: string
|
||||||
annotation_author_name: string
|
annotation_author_name: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type InputForm = {
|
||||||
|
type: InputVarType
|
||||||
|
label: string
|
||||||
|
variable: any
|
||||||
|
required: boolean
|
||||||
|
[key: string]: any
|
||||||
|
}
|
||||||
|
16
web/app/components/base/chat/chat/utils.ts
Normal file
16
web/app/components/base/chat/chat/utils.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import type { InputForm } from './type'
|
||||||
|
|
||||||
|
export const processOpeningStatement = (openingStatement: string, inputs: Record<string, any>, inputsForm: InputForm[]) => {
|
||||||
|
if (!openingStatement)
|
||||||
|
return openingStatement
|
||||||
|
|
||||||
|
return openingStatement.replace(/\{\{([^}]+)\}\}/g, (match, key) => {
|
||||||
|
const name = inputs[key]
|
||||||
|
if (name) { // has set value
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
const valueObj = inputsForm.find(v => v.variable === key)
|
||||||
|
return valueObj ? `{{${valueObj.label}}}` : match
|
||||||
|
})
|
||||||
|
}
|
@ -74,12 +74,13 @@ const FileItem = ({
|
|||||||
</div>
|
</div>
|
||||||
{
|
{
|
||||||
showDownloadAction && (
|
showDownloadAction && (
|
||||||
<ActionButton
|
<a href={file.url} download={true} target='_blank'>
|
||||||
size='xs'
|
<ActionButton
|
||||||
onClick={() => window.open(file.url, '_blank')}
|
size='xs'
|
||||||
>
|
>
|
||||||
<RiDownloadLine className='w-3.5 h-3.5 text-text-tertiary' />
|
<RiDownloadLine className='w-3.5 h-3.5 text-text-tertiary' />
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
|
</a>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
@ -1,48 +1,14 @@
|
|||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import { useStoreApi } from 'reactflow'
|
import { useStoreApi } from 'reactflow'
|
||||||
import { useWorkflowStore } from '@/app/components/workflow/store'
|
|
||||||
import {
|
import {
|
||||||
BlockEnum,
|
BlockEnum,
|
||||||
InputVarType,
|
InputVarType,
|
||||||
} from '@/app/components/workflow/types'
|
} from '@/app/components/workflow/types'
|
||||||
import { useToastContext } from '@/app/components/base/toast'
|
|
||||||
import type { InputVar } from '@/app/components/workflow/types'
|
import type { InputVar } from '@/app/components/workflow/types'
|
||||||
import { getProcessedFiles } from '@/app/components/base/file-uploader/utils'
|
import { getProcessedFiles } from '@/app/components/base/file-uploader/utils'
|
||||||
|
|
||||||
export const useCheckStartNodeForm = () => {
|
export const useCheckStartNodeForm = () => {
|
||||||
const { t } = useTranslation()
|
|
||||||
const storeApi = useStoreApi()
|
const storeApi = useStoreApi()
|
||||||
const workflowStore = useWorkflowStore()
|
|
||||||
const { notify } = useToastContext()
|
|
||||||
|
|
||||||
const checkStartNodeForm = useCallback(() => {
|
|
||||||
const { getNodes } = storeApi.getState()
|
|
||||||
const nodes = getNodes()
|
|
||||||
const startNode = nodes.find(node => node.data.type === BlockEnum.Start)
|
|
||||||
const variables: InputVar[] = startNode?.data.variables || []
|
|
||||||
const inputs = workflowStore.getState().inputs
|
|
||||||
|
|
||||||
let hasEmptyInput = ''
|
|
||||||
const requiredVars = variables.filter(({ required }) => required)
|
|
||||||
|
|
||||||
if (requiredVars?.length) {
|
|
||||||
requiredVars.forEach(({ variable, label }) => {
|
|
||||||
if (hasEmptyInput)
|
|
||||||
return
|
|
||||||
|
|
||||||
if (!inputs[variable])
|
|
||||||
hasEmptyInput = label as string
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasEmptyInput) {
|
|
||||||
notify({ type: 'error', message: t('appDebug.errorMessage.valueOfVarRequired', { key: hasEmptyInput }) })
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}, [storeApi, workflowStore, notify, t])
|
|
||||||
|
|
||||||
const getProcessedInputs = useCallback((inputs: Record<string, any>) => {
|
const getProcessedInputs = useCallback((inputs: Record<string, any>) => {
|
||||||
const { getNodes } = storeApi.getState()
|
const { getNodes } = storeApi.getState()
|
||||||
@ -53,10 +19,10 @@ export const useCheckStartNodeForm = () => {
|
|||||||
const processedInputs = { ...inputs }
|
const processedInputs = { ...inputs }
|
||||||
|
|
||||||
variables.forEach((variable) => {
|
variables.forEach((variable) => {
|
||||||
if (variable.type === InputVarType.multiFiles)
|
if (variable.type === InputVarType.multiFiles && inputs[variable.variable])
|
||||||
processedInputs[variable.variable] = getProcessedFiles(inputs[variable.variable])
|
processedInputs[variable.variable] = getProcessedFiles(inputs[variable.variable])
|
||||||
|
|
||||||
if (variable.type === InputVarType.singleFile)
|
if (variable.type === InputVarType.singleFile && inputs[variable.variable])
|
||||||
processedInputs[variable.variable] = getProcessedFiles([inputs[variable.variable]])[0]
|
processedInputs[variable.variable] = getProcessedFiles([inputs[variable.variable]])[0]
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -64,7 +30,6 @@ export const useCheckStartNodeForm = () => {
|
|||||||
}, [storeApi])
|
}, [storeApi])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
checkStartNodeForm,
|
|
||||||
getProcessedInputs,
|
getProcessedInputs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,10 +62,7 @@ const ChatWrapper = forwardRef<ChatWrapperRefType, ChatWrapperProps>(({
|
|||||||
}
|
}
|
||||||
}, [features.opening, features.suggested, features.text2speech, features.speech2text, features.citation, features.moderation, features.file])
|
}, [features.opening, features.suggested, features.text2speech, features.speech2text, features.citation, features.moderation, features.file])
|
||||||
const setShowFeaturesPanel = useStore(s => s.setShowFeaturesPanel)
|
const setShowFeaturesPanel = useStore(s => s.setShowFeaturesPanel)
|
||||||
const {
|
const { getProcessedInputs } = useCheckStartNodeForm()
|
||||||
checkStartNodeForm,
|
|
||||||
getProcessedInputs,
|
|
||||||
} = useCheckStartNodeForm()
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
conversationId,
|
conversationId,
|
||||||
@ -81,7 +78,7 @@ const ChatWrapper = forwardRef<ChatWrapperRefType, ChatWrapperProps>(({
|
|||||||
config,
|
config,
|
||||||
{
|
{
|
||||||
inputs,
|
inputs,
|
||||||
promptVariables: (startVariables as any) || [],
|
inputsForm: (startVariables || []) as any,
|
||||||
},
|
},
|
||||||
[],
|
[],
|
||||||
taskId => stopChatMessageResponding(appDetail!.id, taskId),
|
taskId => stopChatMessageResponding(appDetail!.id, taskId),
|
||||||
@ -146,7 +143,8 @@ const ChatWrapper = forwardRef<ChatWrapperRefType, ChatWrapperProps>(({
|
|||||||
showFeatureBar
|
showFeatureBar
|
||||||
onFeatureBarClick={setShowFeaturesPanel}
|
onFeatureBarClick={setShowFeaturesPanel}
|
||||||
onSend={doSend}
|
onSend={doSend}
|
||||||
onSendCheck={checkStartNodeForm}
|
inputs={inputs}
|
||||||
|
inputsForm={(startVariables || []) as any}
|
||||||
onRegenerate={doRegenerate}
|
onRegenerate={doRegenerate}
|
||||||
onStopResponding={handleStop}
|
onStopResponding={handleStop}
|
||||||
chatNode={(
|
chatNode={(
|
||||||
|
@ -12,11 +12,11 @@ import { NodeRunningStatus, WorkflowRunningStatus } from '../../types'
|
|||||||
import type {
|
import type {
|
||||||
ChatItem,
|
ChatItem,
|
||||||
Inputs,
|
Inputs,
|
||||||
PromptVariable,
|
|
||||||
} from '@/app/components/base/chat/types'
|
} from '@/app/components/base/chat/types'
|
||||||
|
import type { InputForm } from '@/app/components/base/chat/chat/type'
|
||||||
|
import { processOpeningStatement } from '@/app/components/base/chat/chat/utils'
|
||||||
import { useToastContext } from '@/app/components/base/toast'
|
import { useToastContext } from '@/app/components/base/toast'
|
||||||
import { TransferMethod } from '@/types/app'
|
import { TransferMethod } from '@/types/app'
|
||||||
import { replaceStringWithValues } from '@/app/components/app/configuration/prompt-value-panel/utils'
|
|
||||||
import {
|
import {
|
||||||
getProcessedFiles,
|
getProcessedFiles,
|
||||||
getProcessedFilesFromResponse,
|
getProcessedFilesFromResponse,
|
||||||
@ -29,9 +29,9 @@ type SendCallback = {
|
|||||||
}
|
}
|
||||||
export const useChat = (
|
export const useChat = (
|
||||||
config: any,
|
config: any,
|
||||||
promptVariablesConfig?: {
|
formSettings?: {
|
||||||
inputs: Inputs
|
inputs: Inputs
|
||||||
promptVariables: PromptVariable[]
|
inputsForm: InputForm[]
|
||||||
},
|
},
|
||||||
prevChatList?: ChatItem[],
|
prevChatList?: ChatItem[],
|
||||||
stopChat?: (taskId: string) => void,
|
stopChat?: (taskId: string) => void,
|
||||||
@ -67,8 +67,8 @@ export const useChat = (
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const getIntroduction = useCallback((str: string) => {
|
const getIntroduction = useCallback((str: string) => {
|
||||||
return replaceStringWithValues(str, promptVariablesConfig?.promptVariables || [], promptVariablesConfig?.inputs || {})
|
return processOpeningStatement(str, formSettings?.inputs || {}, formSettings?.inputsForm || [])
|
||||||
}, [promptVariablesConfig?.inputs, promptVariablesConfig?.promptVariables])
|
}, [formSettings?.inputs, formSettings?.inputsForm])
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (config?.opening_statement) {
|
if (config?.opening_statement) {
|
||||||
handleUpdateChatList(produce(chatListRef.current, (draft) => {
|
handleUpdateChatList(produce(chatListRef.current, (draft) => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user