mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-18 17:35:55 +08:00
file in chat question
This commit is contained in:
parent
396a240e68
commit
2b0c39ed3f
@ -71,12 +71,7 @@ const ChatInputArea = ({
|
|||||||
notify({ type: 'info', message: t('appAnnotation.errorMessage.queryRequired') })
|
notify({ type: 'info', message: t('appAnnotation.errorMessage.queryRequired') })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
onSend(query, files.filter(file => file.progress !== -1).map(fileItem => ({
|
onSend(query, files)
|
||||||
type: fileItem.fileType,
|
|
||||||
transfer_method: fileItem.type,
|
|
||||||
url: fileItem.url || '',
|
|
||||||
upload_file_id: fileItem.fileStorageId || '',
|
|
||||||
})))
|
|
||||||
setQuery('')
|
setQuery('')
|
||||||
setFiles([])
|
setFiles([])
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ import type {
|
|||||||
ChatItem,
|
ChatItem,
|
||||||
Inputs,
|
Inputs,
|
||||||
PromptVariable,
|
PromptVariable,
|
||||||
VisionFile,
|
|
||||||
} from '../types'
|
} from '../types'
|
||||||
import { TransferMethod } from '@/types/app'
|
import { TransferMethod } from '@/types/app'
|
||||||
import { useToastContext } from '@/app/components/base/toast'
|
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 { WorkflowRunningStatus } from '@/app/components/workflow/types'
|
||||||
import useTimestamp from '@/hooks/use-timestamp'
|
import useTimestamp from '@/hooks/use-timestamp'
|
||||||
import { AudioPlayerManager } from '@/app/components/base/audio-btn/audio.player.manager'
|
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 GetAbortController = (abortController: AbortController) => void
|
||||||
type SendCallback = {
|
type SendCallback = {
|
||||||
@ -196,7 +197,11 @@ export const useChat = (
|
|||||||
|
|
||||||
const handleSend = useCallback(async (
|
const handleSend = useCallback(async (
|
||||||
url: string,
|
url: string,
|
||||||
data: any,
|
data: {
|
||||||
|
query: string
|
||||||
|
files?: FileEntity[]
|
||||||
|
[key: string]: any
|
||||||
|
},
|
||||||
{
|
{
|
||||||
onGetConversationMessages,
|
onGetConversationMessages,
|
||||||
onGetSuggestedQuestions,
|
onGetSuggestedQuestions,
|
||||||
@ -244,13 +249,16 @@ export const useChat = (
|
|||||||
handleResponding(true)
|
handleResponding(true)
|
||||||
hasStopResponded.current = false
|
hasStopResponded.current = false
|
||||||
|
|
||||||
|
const { query, files, ...restData } = data
|
||||||
const bodyParams = {
|
const bodyParams = {
|
||||||
response_mode: 'streaming',
|
response_mode: 'streaming',
|
||||||
conversation_id: conversationId.current,
|
conversation_id: conversationId.current,
|
||||||
...data,
|
files: getProcessedFiles(files || []),
|
||||||
|
query,
|
||||||
|
...restData,
|
||||||
}
|
}
|
||||||
if (bodyParams?.files?.length) {
|
if (bodyParams?.files?.length) {
|
||||||
bodyParams.files = bodyParams.files.map((item: VisionFile) => {
|
bodyParams.files = bodyParams.files.map((item) => {
|
||||||
if (item.transfer_method === TransferMethod.local_file) {
|
if (item.transfer_method === TransferMethod.local_file) {
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
|
@ -10,7 +10,7 @@ import type { Theme } from '../embedded-chatbot/theme/theme-context'
|
|||||||
import { CssTransform } from '../embedded-chatbot/theme/utils'
|
import { CssTransform } from '../embedded-chatbot/theme/utils'
|
||||||
import { User } from '@/app/components/base/icons/src/public/avatar'
|
import { User } from '@/app/components/base/icons/src/public/avatar'
|
||||||
import { Markdown } from '@/app/components/base/markdown'
|
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 = {
|
type QuestionProps = {
|
||||||
item: ChatItem
|
item: ChatItem
|
||||||
@ -27,7 +27,6 @@ const Question: FC<QuestionProps> = ({
|
|||||||
message_files,
|
message_files,
|
||||||
} = item
|
} = item
|
||||||
|
|
||||||
const imgSrcs = message_files?.length ? message_files.map(item => item.url) : []
|
|
||||||
return (
|
return (
|
||||||
<div className='flex justify-end mb-2 last:mb-0 pl-10'>
|
<div className='flex justify-end mb-2 last:mb-0 pl-10'>
|
||||||
<div className='group relative mr-4'>
|
<div className='group relative mr-4'>
|
||||||
@ -36,8 +35,12 @@ const Question: FC<QuestionProps> = ({
|
|||||||
style={theme?.chatBubbleColorStyle ? CssTransform(theme.chatBubbleColorStyle) : {}}
|
style={theme?.chatBubbleColorStyle ? CssTransform(theme.chatBubbleColorStyle) : {}}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
!!imgSrcs.length && (
|
!!message_files?.length && (
|
||||||
<ImageGallery srcs={imgSrcs} />
|
<FileList
|
||||||
|
files={message_files}
|
||||||
|
showDeleteAction={false}
|
||||||
|
showDownloadAction={false}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
<Markdown content={content} />
|
<Markdown content={content} />
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import type { TypeWithI18N } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
import type { TypeWithI18N } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
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'
|
||||||
|
|
||||||
export type MessageMore = {
|
export type MessageMore = {
|
||||||
time: string
|
time: string
|
||||||
@ -90,7 +91,7 @@ export type IChatItem = {
|
|||||||
suggestedQuestions?: string[]
|
suggestedQuestions?: string[]
|
||||||
log?: { role: string; text: string; files?: VisionFile[] }[]
|
log?: { role: string; text: string; files?: VisionFile[] }[]
|
||||||
agent_thoughts?: ThoughtItem[]
|
agent_thoughts?: ThoughtItem[]
|
||||||
message_files?: VisionFile[]
|
message_files?: FileEntity[]
|
||||||
workflow_run_id?: string
|
workflow_run_id?: string
|
||||||
// for agent log
|
// for agent log
|
||||||
conversationId?: string
|
conversationId?: string
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import type {
|
import type {
|
||||||
ModelConfig,
|
ModelConfig,
|
||||||
VisionFile,
|
|
||||||
VisionSettings,
|
VisionSettings,
|
||||||
} from '@/types/app'
|
} from '@/types/app'
|
||||||
import type { IChatItem } from '@/app/components/base/chat/chat/type'
|
import type { IChatItem } from '@/app/components/base/chat/chat/type'
|
||||||
import type { NodeTracing } from '@/types/workflow'
|
import type { NodeTracing } from '@/types/workflow'
|
||||||
import type { WorkflowRunningStatus } from '@/app/components/workflow/types'
|
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 type { VisionFile } from '@/types/app'
|
||||||
export { TransferMethod } from '@/types/app'
|
export { TransferMethod } from '@/types/app'
|
||||||
@ -63,7 +63,7 @@ export type ChatItem = IChatItem & {
|
|||||||
conversationId?: string
|
conversationId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OnSend = (message: string, files?: VisionFile[]) => void
|
export type OnSend = (message: string, files?: FileEntity[]) => void
|
||||||
|
|
||||||
export type Callback = {
|
export type Callback = {
|
||||||
onSuccess: () => void
|
onSuccess: () => void
|
||||||
|
@ -1,22 +1,25 @@
|
|||||||
import { isImage } from '../utils'
|
import { isImage } from '../utils'
|
||||||
import { useFile } from '../hooks'
|
import { useFile } from '../hooks'
|
||||||
import { useStore } from '../store'
|
import { useStore } from '../store'
|
||||||
|
import type { FileEntity } from '../types'
|
||||||
import FileImageItem from './file-image-item'
|
import FileImageItem from './file-image-item'
|
||||||
import FileItem from './file-item'
|
import FileItem from './file-item'
|
||||||
import type { FileUpload } from '@/app/components/base/features/types'
|
import type { FileUpload } from '@/app/components/base/features/types'
|
||||||
|
|
||||||
type FileListProps = {
|
type FileListProps = {
|
||||||
fileConfig: FileUpload
|
files: FileEntity[]
|
||||||
|
onRemove?: (fileId: string) => void
|
||||||
|
onReUpload?: (fileId: string) => void
|
||||||
|
showDeleteAction?: boolean
|
||||||
|
showDownloadAction?: boolean
|
||||||
}
|
}
|
||||||
const FileList = ({
|
export const FileList = ({
|
||||||
fileConfig,
|
files,
|
||||||
|
onReUpload,
|
||||||
|
onRemove,
|
||||||
|
showDeleteAction = true,
|
||||||
|
showDownloadAction = false,
|
||||||
}: FileListProps) => {
|
}: FileListProps) => {
|
||||||
const files = useStore(s => s.files)
|
|
||||||
const {
|
|
||||||
handleRemoveFile,
|
|
||||||
handleReUploadFile,
|
|
||||||
} = useFile(fileConfig)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-wrap gap-2'>
|
<div className='flex flex-wrap gap-2'>
|
||||||
{
|
{
|
||||||
@ -28,9 +31,9 @@ const FileList = ({
|
|||||||
fileId={file.fileId}
|
fileId={file.fileId}
|
||||||
imageUrl={file.base64Url}
|
imageUrl={file.base64Url}
|
||||||
progress={file.progress}
|
progress={file.progress}
|
||||||
showDeleteAction
|
showDeleteAction={showDeleteAction}
|
||||||
onRemove={handleRemoveFile}
|
onRemove={onRemove}
|
||||||
onReUpload={handleReUploadFile}
|
onReUpload={onReUpload}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -41,10 +44,10 @@ const FileList = ({
|
|||||||
fileId={file.fileId}
|
fileId={file.fileId}
|
||||||
file={file.file}
|
file={file.file}
|
||||||
progress={file.progress}
|
progress={file.progress}
|
||||||
showDeleteAction
|
showDeleteAction={showDeleteAction}
|
||||||
showDownloadAction={false}
|
showDownloadAction={showDownloadAction}
|
||||||
onRemove={handleRemoveFile}
|
onRemove={onRemove}
|
||||||
onReUpload={handleReUploadFile}
|
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 (
|
||||||
|
<FileList
|
||||||
|
files={files}
|
||||||
|
onReUpload={handleReUploadFile}
|
||||||
|
onRemove={handleRemoveFile}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -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 FileItemInAttachment } from './file-uploader-in-attachment/file-item'
|
||||||
export { default as FileUploaderInChatInput } from './file-uploader-in-chat-input'
|
export { default as FileUploaderInChatInput } from './file-uploader-in-chat-input'
|
||||||
export { default as FileTypeIcon } from './file-type-icon'
|
export { default as FileTypeIcon } from './file-type-icon'
|
||||||
export { default as FileListInChatInput } from './file-uploader-in-chat-input/file-list'
|
export { FileListInChatInput } from './file-uploader-in-chat-input/file-list'
|
||||||
export { default as FileItemInChatInput } 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'
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { FileAppearanceTypeEnum } from './types'
|
import { FileAppearanceTypeEnum } from './types'
|
||||||
|
import type { FileEntity } from './types'
|
||||||
import { upload } from '@/service/base'
|
import { upload } from '@/service/base'
|
||||||
import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
|
import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
|
||||||
|
|
||||||
@ -83,3 +84,12 @@ export const getFileType = (file?: File) => {
|
|||||||
|
|
||||||
return ''
|
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 || '',
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
@ -407,9 +407,9 @@ const WorkflowWrap = memo(() => {
|
|||||||
const initialFeatures: FeaturesData = {
|
const initialFeatures: FeaturesData = {
|
||||||
file: {
|
file: {
|
||||||
image: {
|
image: {
|
||||||
enabled: !!features.file_upload?.image.enabled,
|
enabled: !!features.file_upload?.image?.enabled,
|
||||||
number_limits: features.file_upload?.image.number_limits || 3,
|
number_limits: features.file_upload?.image?.number_limits || 3,
|
||||||
transfer_methods: features.file_upload?.image.transfer_methods || ['local_file', 'remote_url'],
|
transfer_methods: features.file_upload?.image?.transfer_methods || ['local_file', 'remote_url'],
|
||||||
},
|
},
|
||||||
enabled: !!(features.file_upload?.enabled || features.file_upload?.image?.enabled),
|
enabled: !!(features.file_upload?.enabled || features.file_upload?.image?.enabled),
|
||||||
allowed_file_types: features.file_upload?.allowed_file_types || [SupportUploadFileTypes.image],
|
allowed_file_types: features.file_upload?.allowed_file_types || [SupportUploadFileTypes.image],
|
||||||
|
@ -15,8 +15,9 @@ import type {
|
|||||||
} from '@/app/components/base/chat/types'
|
} from '@/app/components/base/chat/types'
|
||||||
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 type { VisionFile } from '@/types/app'
|
|
||||||
import { replaceStringWithValues } from '@/app/components/app/configuration/prompt-value-panel/utils'
|
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 GetAbortController = (abortController: AbortController) => void
|
||||||
type SendCallback = {
|
type SendCallback = {
|
||||||
@ -143,7 +144,11 @@ export const useChat = (
|
|||||||
}, [handleUpdateChatList])
|
}, [handleUpdateChatList])
|
||||||
|
|
||||||
const handleSend = useCallback((
|
const handleSend = useCallback((
|
||||||
params: any,
|
params: {
|
||||||
|
query: string
|
||||||
|
files?: FileEntity[]
|
||||||
|
[key: string]: any
|
||||||
|
},
|
||||||
{
|
{
|
||||||
onGetSuggestedQuestions,
|
onGetSuggestedQuestions,
|
||||||
}: SendCallback,
|
}: SendCallback,
|
||||||
@ -182,12 +187,14 @@ export const useChat = (
|
|||||||
|
|
||||||
handleResponding(true)
|
handleResponding(true)
|
||||||
|
|
||||||
|
const { files, ...restParams } = params
|
||||||
const bodyParams = {
|
const bodyParams = {
|
||||||
conversation_id: conversationId.current,
|
conversation_id: conversationId.current,
|
||||||
...params,
|
files: getProcessedFiles(files || []),
|
||||||
|
...restParams,
|
||||||
}
|
}
|
||||||
if (bodyParams?.files?.length) {
|
if (bodyParams?.files?.length) {
|
||||||
bodyParams.files = bodyParams.files.map((item: VisionFile) => {
|
bodyParams.files = bodyParams.files.map((item) => {
|
||||||
if (item.transfer_method === TransferMethod.local_file) {
|
if (item.transfer_method === TransferMethod.local_file) {
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user