diff --git a/web/app/components/base/chat/chat/chat-input-area/index.tsx b/web/app/components/base/chat/chat/chat-input-area/index.tsx index f109e4d3b2..2d1f868a26 100644 --- a/web/app/components/base/chat/chat/chat-input-area/index.tsx +++ b/web/app/components/base/chat/chat/chat-input-area/index.tsx @@ -71,12 +71,7 @@ const ChatInputArea = ({ notify({ type: 'info', message: t('appAnnotation.errorMessage.queryRequired') }) return } - onSend(query, files.filter(file => file.progress !== -1).map(fileItem => ({ - type: fileItem.fileType, - transfer_method: fileItem.type, - url: fileItem.url || '', - upload_file_id: fileItem.fileStorageId || '', - }))) + onSend(query, files) setQuery('') setFiles([]) } diff --git a/web/app/components/base/chat/chat/hooks.ts b/web/app/components/base/chat/chat/hooks.ts index 56827eb72d..ac77080749 100644 --- a/web/app/components/base/chat/chat/hooks.ts +++ b/web/app/components/base/chat/chat/hooks.ts @@ -13,7 +13,6 @@ import type { ChatItem, Inputs, PromptVariable, - VisionFile, } from '../types' import { TransferMethod } from '@/types/app' import { useToastContext } from '@/app/components/base/toast' @@ -23,6 +22,8 @@ import type { Annotation } from '@/models/log' import { WorkflowRunningStatus } from '@/app/components/workflow/types' import useTimestamp from '@/hooks/use-timestamp' import { AudioPlayerManager } from '@/app/components/base/audio-btn/audio.player.manager' +import type { FileEntity } from '@/app/components/base/file-uploader/types' +import { getProcessedFiles } from '@/app/components/base/file-uploader/utils' type GetAbortController = (abortController: AbortController) => void type SendCallback = { @@ -196,7 +197,11 @@ export const useChat = ( const handleSend = useCallback(async ( url: string, - data: any, + data: { + query: string + files?: FileEntity[] + [key: string]: any + }, { onGetConversationMessages, onGetSuggestedQuestions, @@ -244,13 +249,16 @@ export const useChat = ( handleResponding(true) hasStopResponded.current = false + const { query, files, ...restData } = data const bodyParams = { response_mode: 'streaming', conversation_id: conversationId.current, - ...data, + files: getProcessedFiles(files || []), + query, + ...restData, } if (bodyParams?.files?.length) { - bodyParams.files = bodyParams.files.map((item: VisionFile) => { + bodyParams.files = bodyParams.files.map((item) => { if (item.transfer_method === TransferMethod.local_file) { return { ...item, diff --git a/web/app/components/base/chat/chat/question.tsx b/web/app/components/base/chat/chat/question.tsx index 30809ec995..a8014b288d 100644 --- a/web/app/components/base/chat/chat/question.tsx +++ b/web/app/components/base/chat/chat/question.tsx @@ -10,7 +10,7 @@ import type { Theme } from '../embedded-chatbot/theme/theme-context' import { CssTransform } from '../embedded-chatbot/theme/utils' import { User } from '@/app/components/base/icons/src/public/avatar' import { Markdown } from '@/app/components/base/markdown' -import ImageGallery from '@/app/components/base/image-gallery' +import { FileList } from '@/app/components/base/file-uploader' type QuestionProps = { item: ChatItem @@ -27,7 +27,6 @@ const Question: FC = ({ message_files, } = item - const imgSrcs = message_files?.length ? message_files.map(item => item.url) : [] return (
@@ -36,8 +35,12 @@ const Question: FC = ({ style={theme?.chatBubbleColorStyle ? CssTransform(theme.chatBubbleColorStyle) : {}} > { - !!imgSrcs.length && ( - + !!message_files?.length && ( + ) } diff --git a/web/app/components/base/chat/chat/type.ts b/web/app/components/base/chat/chat/type.ts index b2cb18011c..c608db89ce 100644 --- a/web/app/components/base/chat/chat/type.ts +++ b/web/app/components/base/chat/chat/type.ts @@ -1,6 +1,7 @@ import type { TypeWithI18N } from '@/app/components/header/account-setting/model-provider-page/declarations' import type { Annotation, MessageRating } from '@/models/log' import type { VisionFile } from '@/types/app' +import type { FileEntity } from '@/app/components/base/file-uploader/types' export type MessageMore = { time: string @@ -90,7 +91,7 @@ export type IChatItem = { suggestedQuestions?: string[] log?: { role: string; text: string; files?: VisionFile[] }[] agent_thoughts?: ThoughtItem[] - message_files?: VisionFile[] + message_files?: FileEntity[] workflow_run_id?: string // for agent log conversationId?: string diff --git a/web/app/components/base/chat/types.ts b/web/app/components/base/chat/types.ts index 21277fec57..2b73774a99 100644 --- a/web/app/components/base/chat/types.ts +++ b/web/app/components/base/chat/types.ts @@ -1,11 +1,11 @@ import type { ModelConfig, - VisionFile, VisionSettings, } from '@/types/app' import type { IChatItem } from '@/app/components/base/chat/chat/type' import type { NodeTracing } from '@/types/workflow' import type { WorkflowRunningStatus } from '@/app/components/workflow/types' +import type { FileEntity } from '@/app/components/base/file-uploader/types' export type { VisionFile } from '@/types/app' export { TransferMethod } from '@/types/app' @@ -63,7 +63,7 @@ export type ChatItem = IChatItem & { conversationId?: string } -export type OnSend = (message: string, files?: VisionFile[]) => void +export type OnSend = (message: string, files?: FileEntity[]) => void export type Callback = { onSuccess: () => void diff --git a/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-list.tsx b/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-list.tsx index 45ec54b560..8ca422e911 100644 --- a/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-list.tsx +++ b/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-list.tsx @@ -1,22 +1,25 @@ import { isImage } from '../utils' import { useFile } from '../hooks' import { useStore } from '../store' +import type { FileEntity } from '../types' import FileImageItem from './file-image-item' import FileItem from './file-item' import type { FileUpload } from '@/app/components/base/features/types' type FileListProps = { - fileConfig: FileUpload + files: FileEntity[] + onRemove?: (fileId: string) => void + onReUpload?: (fileId: string) => void + showDeleteAction?: boolean + showDownloadAction?: boolean } -const FileList = ({ - fileConfig, +export const FileList = ({ + files, + onReUpload, + onRemove, + showDeleteAction = true, + showDownloadAction = false, }: FileListProps) => { - const files = useStore(s => s.files) - const { - handleRemoveFile, - handleReUploadFile, - } = useFile(fileConfig) - return (
{ @@ -28,9 +31,9 @@ const FileList = ({ fileId={file.fileId} imageUrl={file.base64Url} progress={file.progress} - showDeleteAction - onRemove={handleRemoveFile} - onReUpload={handleReUploadFile} + showDeleteAction={showDeleteAction} + onRemove={onRemove} + onReUpload={onReUpload} /> ) } @@ -41,10 +44,10 @@ const FileList = ({ fileId={file.fileId} file={file.file} progress={file.progress} - showDeleteAction - showDownloadAction={false} - onRemove={handleRemoveFile} - onReUpload={handleReUploadFile} + showDeleteAction={showDeleteAction} + showDownloadAction={showDownloadAction} + onRemove={onRemove} + onReUpload={onReUpload} /> ) }) @@ -53,4 +56,23 @@ const FileList = ({ ) } -export default FileList +type FileListInChatInputProps = { + fileConfig: FileUpload +} +export const FileListInChatInput = ({ + fileConfig, +}: FileListInChatInputProps) => { + const files = useStore(s => s.files) + const { + handleRemoveFile, + handleReUploadFile, + } = useFile(fileConfig) + + return ( + + ) +} diff --git a/web/app/components/base/file-uploader/index.ts b/web/app/components/base/file-uploader/index.ts index 1c599cd4b0..ff5914cf56 100644 --- a/web/app/components/base/file-uploader/index.ts +++ b/web/app/components/base/file-uploader/index.ts @@ -2,5 +2,6 @@ export { default as FileUploaderInAttachmentWrapper } from './file-uploader-in-a export { default as FileItemInAttachment } from './file-uploader-in-attachment/file-item' export { default as FileUploaderInChatInput } from './file-uploader-in-chat-input' export { default as FileTypeIcon } from './file-type-icon' -export { default as FileListInChatInput } from './file-uploader-in-chat-input/file-list' -export { default as FileItemInChatInput } from './file-uploader-in-chat-input/file-list' +export { FileListInChatInput } from './file-uploader-in-chat-input/file-list' +export { FileList } from './file-uploader-in-chat-input/file-list' +export { default as FileItem } from './file-uploader-in-chat-input/file-item' diff --git a/web/app/components/base/file-uploader/utils.ts b/web/app/components/base/file-uploader/utils.ts index c4fc5bc94b..b9b6f73fcd 100644 --- a/web/app/components/base/file-uploader/utils.ts +++ b/web/app/components/base/file-uploader/utils.ts @@ -1,4 +1,5 @@ import { FileAppearanceTypeEnum } from './types' +import type { FileEntity } from './types' import { upload } from '@/service/base' import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants' @@ -83,3 +84,12 @@ export const getFileType = (file?: File) => { return '' } + +export const getProcessedFiles = (files: FileEntity[]) => { + return files.filter(file => file.progress !== -1).map(fileItem => ({ + type: fileItem.fileType, + transfer_method: fileItem.type, + url: fileItem.url || '', + upload_file_id: fileItem.fileStorageId || '', + })) +} diff --git a/web/app/components/workflow/index.tsx b/web/app/components/workflow/index.tsx index fae62f6b08..95b4f09778 100644 --- a/web/app/components/workflow/index.tsx +++ b/web/app/components/workflow/index.tsx @@ -407,9 +407,9 @@ const WorkflowWrap = memo(() => { const initialFeatures: FeaturesData = { file: { image: { - enabled: !!features.file_upload?.image.enabled, - number_limits: features.file_upload?.image.number_limits || 3, - transfer_methods: features.file_upload?.image.transfer_methods || ['local_file', 'remote_url'], + enabled: !!features.file_upload?.image?.enabled, + number_limits: features.file_upload?.image?.number_limits || 3, + transfer_methods: features.file_upload?.image?.transfer_methods || ['local_file', 'remote_url'], }, enabled: !!(features.file_upload?.enabled || features.file_upload?.image?.enabled), allowed_file_types: features.file_upload?.allowed_file_types || [SupportUploadFileTypes.image], 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 aaf1b95082..a693b5f20c 100644 --- a/web/app/components/workflow/panel/debug-and-preview/hooks.ts +++ b/web/app/components/workflow/panel/debug-and-preview/hooks.ts @@ -15,8 +15,9 @@ import type { } from '@/app/components/base/chat/types' import { useToastContext } from '@/app/components/base/toast' import { TransferMethod } from '@/types/app' -import type { VisionFile } from '@/types/app' import { replaceStringWithValues } from '@/app/components/app/configuration/prompt-value-panel/utils' +import { getProcessedFiles } from '@/app/components/base/file-uploader/utils' +import type { FileEntity } from '@/app/components/base/file-uploader/types' type GetAbortController = (abortController: AbortController) => void type SendCallback = { @@ -143,7 +144,11 @@ export const useChat = ( }, [handleUpdateChatList]) const handleSend = useCallback(( - params: any, + params: { + query: string + files?: FileEntity[] + [key: string]: any + }, { onGetSuggestedQuestions, }: SendCallback, @@ -182,12 +187,14 @@ export const useChat = ( handleResponding(true) + const { files, ...restParams } = params const bodyParams = { conversation_id: conversationId.current, - ...params, + files: getProcessedFiles(files || []), + ...restParams, } if (bodyParams?.files?.length) { - bodyParams.files = bodyParams.files.map((item: VisionFile) => { + bodyParams.files = bodyParams.files.map((item) => { if (item.transfer_method === TransferMethod.local_file) { return { ...item,