mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-08-02 04:30:38 +08:00
### What problem does this PR solve? feat: Create a conversation before uploading files in it #1880 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
parent
35e880c432
commit
c739b68b29
@ -1,11 +1,10 @@
|
|||||||
import { Authorization } from '@/constants/authorization';
|
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
import { useTranslate } from '@/hooks/common-hooks';
|
||||||
import {
|
import {
|
||||||
useDeleteDocument,
|
useDeleteDocument,
|
||||||
useFetchDocumentInfosByIds,
|
useFetchDocumentInfosByIds,
|
||||||
useRemoveNextDocument,
|
useRemoveNextDocument,
|
||||||
|
useUploadAndParseDocument,
|
||||||
} from '@/hooks/document-hooks';
|
} from '@/hooks/document-hooks';
|
||||||
import { getAuthorization } from '@/utils/authorization-util';
|
|
||||||
import { getExtension } from '@/utils/document-util';
|
import { getExtension } from '@/utils/document-util';
|
||||||
import { formatBytes } from '@/utils/file-util';
|
import { formatBytes } from '@/utils/file-util';
|
||||||
import {
|
import {
|
||||||
@ -28,7 +27,14 @@ import {
|
|||||||
} from 'antd';
|
} from 'antd';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import { ChangeEventHandler, useCallback, useEffect, useState } from 'react';
|
import {
|
||||||
|
ChangeEventHandler,
|
||||||
|
memo,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
import FileIcon from '../file-icon';
|
import FileIcon from '../file-icon';
|
||||||
import SvgIcon from '../svg-icon';
|
import SvgIcon from '../svg-icon';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
@ -64,9 +70,10 @@ interface IProps {
|
|||||||
onPressEnter(documentIds: string[]): void;
|
onPressEnter(documentIds: string[]): void;
|
||||||
onInputChange: ChangeEventHandler<HTMLInputElement>;
|
onInputChange: ChangeEventHandler<HTMLInputElement>;
|
||||||
conversationId: string;
|
conversationId: string;
|
||||||
uploadUrl?: string;
|
uploadMethod?: string;
|
||||||
isShared?: boolean;
|
isShared?: boolean;
|
||||||
showUploadIcon?: boolean;
|
showUploadIcon?: boolean;
|
||||||
|
createConversationBeforeUploadDocument?(message: string): Promise<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getBase64 = (file: FileType): Promise<string> =>
|
const getBase64 = (file: FileType): Promise<string> =>
|
||||||
@ -87,12 +94,15 @@ const MessageInput = ({
|
|||||||
onInputChange,
|
onInputChange,
|
||||||
conversationId,
|
conversationId,
|
||||||
showUploadIcon = true,
|
showUploadIcon = true,
|
||||||
uploadUrl = '/v1/document/upload_and_parse',
|
createConversationBeforeUploadDocument,
|
||||||
|
uploadMethod = 'upload_and_parse',
|
||||||
}: IProps) => {
|
}: IProps) => {
|
||||||
const { t } = useTranslate('chat');
|
const { t } = useTranslate('chat');
|
||||||
const { removeDocument } = useRemoveNextDocument();
|
const { removeDocument } = useRemoveNextDocument();
|
||||||
const { deleteDocument } = useDeleteDocument();
|
const { deleteDocument } = useDeleteDocument();
|
||||||
const { data: documentInfos, setDocumentIds } = useFetchDocumentInfosByIds();
|
const { data: documentInfos, setDocumentIds } = useFetchDocumentInfosByIds();
|
||||||
|
const { uploadAndParseDocument } = useUploadAndParseDocument(uploadMethod);
|
||||||
|
const conversationIdRef = useRef(conversationId);
|
||||||
|
|
||||||
const [fileList, setFileList] = useState<UploadFile[]>([]);
|
const [fileList, setFileList] = useState<UploadFile[]>([]);
|
||||||
|
|
||||||
@ -102,9 +112,44 @@ const MessageInput = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChange: UploadProps['onChange'] = ({ fileList: newFileList }) => {
|
const handleChange: UploadProps['onChange'] = async ({
|
||||||
setFileList(newFileList);
|
// fileList: newFileList,
|
||||||
|
file,
|
||||||
|
}) => {
|
||||||
|
let nextConversationId: string = conversationId;
|
||||||
|
if (createConversationBeforeUploadDocument && !conversationId) {
|
||||||
|
const creatingRet = await createConversationBeforeUploadDocument(
|
||||||
|
file.name,
|
||||||
|
);
|
||||||
|
if (creatingRet.retcode === 0) {
|
||||||
|
nextConversationId = creatingRet.data.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setFileList((list) => {
|
||||||
|
list.push({
|
||||||
|
...file,
|
||||||
|
status: 'uploading',
|
||||||
|
originFileObj: file as any,
|
||||||
|
});
|
||||||
|
return [...list];
|
||||||
|
});
|
||||||
|
const ret = await uploadAndParseDocument({
|
||||||
|
conversationId: nextConversationId,
|
||||||
|
fileList: [file],
|
||||||
|
});
|
||||||
|
setFileList((list) => {
|
||||||
|
const nextList = list.filter((x) => x.uid !== file.uid);
|
||||||
|
nextList.push({
|
||||||
|
...file,
|
||||||
|
originFileObj: file as any,
|
||||||
|
response: ret,
|
||||||
|
percent: 100,
|
||||||
|
status: ret?.retcode === 0 ? 'done' : 'error',
|
||||||
|
});
|
||||||
|
return nextList;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const isUploadingFile = fileList.some((x) => x.status === 'uploading');
|
const isUploadingFile = fileList.some((x) => x.status === 'uploading');
|
||||||
|
|
||||||
const handlePressEnter = useCallback(async () => {
|
const handlePressEnter = useCallback(async () => {
|
||||||
@ -150,6 +195,16 @@ const MessageInput = ({
|
|||||||
setDocumentIds(ids);
|
setDocumentIds(ids);
|
||||||
}, [fileList, setDocumentIds]);
|
}, [fileList, setDocumentIds]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
conversationIdRef.current &&
|
||||||
|
conversationId !== conversationIdRef.current
|
||||||
|
) {
|
||||||
|
setFileList([]);
|
||||||
|
}
|
||||||
|
conversationIdRef.current = conversationId;
|
||||||
|
}, [conversationId, setFileList]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex gap={20} vertical className={styles.messageInputWrapper}>
|
<Flex gap={20} vertical className={styles.messageInputWrapper}>
|
||||||
<Input
|
<Input
|
||||||
@ -160,18 +215,22 @@ const MessageInput = ({
|
|||||||
className={classNames({ [styles.inputWrapper]: fileList.length === 0 })}
|
className={classNames({ [styles.inputWrapper]: fileList.length === 0 })}
|
||||||
suffix={
|
suffix={
|
||||||
<Space>
|
<Space>
|
||||||
{conversationId && showUploadIcon && (
|
{showUploadIcon && (
|
||||||
<Upload
|
<Upload
|
||||||
action={uploadUrl}
|
// action={uploadUrl}
|
||||||
fileList={fileList}
|
// fileList={fileList}
|
||||||
onPreview={handlePreview}
|
onPreview={handlePreview}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
multiple
|
multiple={false}
|
||||||
headers={{ [Authorization]: getAuthorization() }}
|
// headers={{ [Authorization]: getAuthorization() }}
|
||||||
data={{ conversation_id: conversationId }}
|
// data={{ conversation_id: conversationId }}
|
||||||
method="post"
|
// method="post"
|
||||||
onRemove={handleRemove}
|
onRemove={handleRemove}
|
||||||
showUploadList={false}
|
showUploadList={false}
|
||||||
|
beforeUpload={(file, fileList) => {
|
||||||
|
console.log('🚀 ~ beforeUpload:', fileList);
|
||||||
|
return false;
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
type={'text'}
|
type={'text'}
|
||||||
@ -209,8 +268,10 @@ const MessageInput = ({
|
|||||||
dataSource={fileList}
|
dataSource={fileList}
|
||||||
className={styles.listWrapper}
|
className={styles.listWrapper}
|
||||||
renderItem={(item) => {
|
renderItem={(item) => {
|
||||||
const fileExtension = getExtension(item.name);
|
|
||||||
const id = getFileId(item);
|
const id = getFileId(item);
|
||||||
|
const documentInfo = getDocumentInfoById(id);
|
||||||
|
const fileExtension = getExtension(documentInfo?.name ?? '');
|
||||||
|
const fileName = item.originFileObj?.name ?? '';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<List.Item>
|
<List.Item>
|
||||||
@ -228,14 +289,14 @@ const MessageInput = ({
|
|||||||
// width={30}
|
// width={30}
|
||||||
></InfoCircleOutlined>
|
></InfoCircleOutlined>
|
||||||
) : (
|
) : (
|
||||||
<FileIcon id={id} name={item.name}></FileIcon>
|
<FileIcon id={id} name={fileName}></FileIcon>
|
||||||
)}
|
)}
|
||||||
<Flex vertical style={{ width: '90%' }}>
|
<Flex vertical style={{ width: '90%' }}>
|
||||||
<Text
|
<Text
|
||||||
ellipsis={{ tooltip: item.name }}
|
ellipsis={{ tooltip: fileName }}
|
||||||
className={styles.nameText}
|
className={styles.nameText}
|
||||||
>
|
>
|
||||||
<b> {item.name}</b>
|
<b> {fileName}</b>
|
||||||
</Text>
|
</Text>
|
||||||
{isUploadError(item) ? (
|
{isUploadError(item) ? (
|
||||||
t('uploadFailed')
|
t('uploadFailed')
|
||||||
@ -275,4 +336,4 @@ const MessageInput = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MessageInput;
|
export default memo(MessageInput);
|
||||||
|
@ -125,6 +125,23 @@ export const useUpdateConversation = () => {
|
|||||||
return updateConversation;
|
return updateConversation;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useUpdateNextConversation = () => {
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isPending: loading,
|
||||||
|
mutateAsync,
|
||||||
|
} = useMutation({
|
||||||
|
mutationKey: ['updateConversation'],
|
||||||
|
mutationFn: async (params: Record<string, any>) => {
|
||||||
|
const { data } = await chatService.setConversation(params);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data, loading, updateConversation: mutateAsync };
|
||||||
|
};
|
||||||
|
|
||||||
export const useSetDialog = () => {
|
export const useSetDialog = () => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { IDocumentInfo } from '@/interfaces/database/document';
|
import { IDocumentInfo } from '@/interfaces/database/document';
|
||||||
import { IChunk, IKnowledgeFile } from '@/interfaces/database/knowledge';
|
import { IChunk, IKnowledgeFile } from '@/interfaces/database/knowledge';
|
||||||
import { IChangeParserConfigRequestBody } from '@/interfaces/request/document';
|
import { IChangeParserConfigRequestBody } from '@/interfaces/request/document';
|
||||||
|
import chatService from '@/services/chat-service';
|
||||||
import kbService from '@/services/knowledge-service';
|
import kbService from '@/services/knowledge-service';
|
||||||
import { api_host } from '@/utils/api';
|
import { api_host } from '@/utils/api';
|
||||||
import { buildChunkHighlights } from '@/utils/document-util';
|
import { buildChunkHighlights } from '@/utils/document-util';
|
||||||
@ -333,3 +334,34 @@ export const useDeleteDocument = () => {
|
|||||||
|
|
||||||
return { data, loading, deleteDocument: mutateAsync };
|
return { data, loading, deleteDocument: mutateAsync };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useUploadAndParseDocument = (uploadMethod: string) => {
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isPending: loading,
|
||||||
|
mutateAsync,
|
||||||
|
} = useMutation({
|
||||||
|
mutationKey: ['uploadAndParseDocument'],
|
||||||
|
mutationFn: async ({
|
||||||
|
conversationId,
|
||||||
|
fileList,
|
||||||
|
}: {
|
||||||
|
conversationId: string;
|
||||||
|
fileList: UploadFile[];
|
||||||
|
}) => {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('conversation_id', conversationId);
|
||||||
|
fileList.forEach((file: UploadFile) => {
|
||||||
|
formData.append('file', file as any);
|
||||||
|
});
|
||||||
|
if (uploadMethod === 'upload_and_parse') {
|
||||||
|
const data = await kbService.upload_and_parse(formData);
|
||||||
|
return data?.data;
|
||||||
|
}
|
||||||
|
const data = await chatService.uploadAndParseExternal(formData);
|
||||||
|
return data?.data;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data, loading, uploadAndParseDocument: mutateAsync };
|
||||||
|
};
|
||||||
|
@ -4,6 +4,7 @@ import { MessageType } from '@/constants/chat';
|
|||||||
import { Drawer, Flex, Spin } from 'antd';
|
import { Drawer, Flex, Spin } from 'antd';
|
||||||
import {
|
import {
|
||||||
useClickDrawer,
|
useClickDrawer,
|
||||||
|
useCreateConversationBeforeUploadDocument,
|
||||||
useFetchConversationOnMount,
|
useFetchConversationOnMount,
|
||||||
useGetFileIcon,
|
useGetFileIcon,
|
||||||
useGetSendButtonDisabled,
|
useGetSendButtonDisabled,
|
||||||
@ -24,6 +25,7 @@ const ChatContainer = () => {
|
|||||||
addNewestConversation,
|
addNewestConversation,
|
||||||
removeLatestMessage,
|
removeLatestMessage,
|
||||||
addNewestAnswer,
|
addNewestAnswer,
|
||||||
|
conversationId,
|
||||||
} = useFetchConversationOnMount();
|
} = useFetchConversationOnMount();
|
||||||
const {
|
const {
|
||||||
handleInputChange,
|
handleInputChange,
|
||||||
@ -43,6 +45,8 @@ const ChatContainer = () => {
|
|||||||
useGetFileIcon();
|
useGetFileIcon();
|
||||||
const loading = useSelectConversationLoading();
|
const loading = useSelectConversationLoading();
|
||||||
const { data: userInfo } = useFetchUserInfo();
|
const { data: userInfo } = useFetchUserInfo();
|
||||||
|
const { createConversationBeforeUploadDocument } =
|
||||||
|
useCreateConversationBeforeUploadDocument();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -78,7 +82,10 @@ const ChatContainer = () => {
|
|||||||
value={value}
|
value={value}
|
||||||
onInputChange={handleInputChange}
|
onInputChange={handleInputChange}
|
||||||
onPressEnter={handlePressEnter}
|
onPressEnter={handlePressEnter}
|
||||||
conversationId={conversation.id}
|
conversationId={conversationId}
|
||||||
|
createConversationBeforeUploadDocument={
|
||||||
|
createConversationBeforeUploadDocument
|
||||||
|
}
|
||||||
></MessageInput>
|
></MessageInput>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Drawer
|
<Drawer
|
||||||
|
@ -520,6 +520,7 @@ export const useFetchConversationOnMount = () => {
|
|||||||
ref,
|
ref,
|
||||||
removeLatestMessage,
|
removeLatestMessage,
|
||||||
addNewestAnswer,
|
addNewestAnswer,
|
||||||
|
conversationId,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -769,4 +770,28 @@ export const useGetSendButtonDisabled = () => {
|
|||||||
export const useSendButtonDisabled = (value: string) => {
|
export const useSendButtonDisabled = (value: string) => {
|
||||||
return trim(value) === '';
|
return trim(value) === '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useCreateConversationBeforeUploadDocument = () => {
|
||||||
|
const { setConversation } = useSetConversation();
|
||||||
|
const { dialogId } = useGetChatSearchParams();
|
||||||
|
|
||||||
|
const { handleClickConversation } = useClickConversationCard();
|
||||||
|
|
||||||
|
const createConversationBeforeUploadDocument = useCallback(
|
||||||
|
async (message: string) => {
|
||||||
|
const data = await setConversation(message);
|
||||||
|
if (data.retcode === 0) {
|
||||||
|
const id = data.data.id;
|
||||||
|
handleClickConversation(id);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
[setConversation, handleClickConversation],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
createConversationBeforeUploadDocument,
|
||||||
|
dialogId,
|
||||||
|
};
|
||||||
|
};
|
||||||
//#endregion
|
//#endregion
|
||||||
|
@ -75,7 +75,7 @@ const ChatContainer = () => {
|
|||||||
onInputChange={handleInputChange}
|
onInputChange={handleInputChange}
|
||||||
onPressEnter={handlePressEnter}
|
onPressEnter={handlePressEnter}
|
||||||
sendLoading={sendLoading}
|
sendLoading={sendLoading}
|
||||||
uploadUrl="/v1/api/document/upload_and_parse"
|
uploadMethod="external_upload_and_parse"
|
||||||
showUploadIcon={from === SharedFrom.Chat}
|
showUploadIcon={from === SharedFrom.Chat}
|
||||||
></MessageInput>
|
></MessageInput>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -19,6 +19,7 @@ const {
|
|||||||
createExternalConversation,
|
createExternalConversation,
|
||||||
getExternalConversation,
|
getExternalConversation,
|
||||||
completeExternalConversation,
|
completeExternalConversation,
|
||||||
|
uploadAndParseExternal,
|
||||||
} = api;
|
} = api;
|
||||||
|
|
||||||
const methods = {
|
const methods = {
|
||||||
@ -86,6 +87,10 @@ const methods = {
|
|||||||
url: completeExternalConversation,
|
url: completeExternalConversation,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
},
|
},
|
||||||
|
uploadAndParseExternal: {
|
||||||
|
url: uploadAndParseExternal,
|
||||||
|
method: 'post',
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const chatService = registerServer<keyof typeof methods>(methods, request);
|
const chatService = registerServer<keyof typeof methods>(methods, request);
|
||||||
|
@ -30,6 +30,7 @@ const {
|
|||||||
web_crawl,
|
web_crawl,
|
||||||
knowledge_graph,
|
knowledge_graph,
|
||||||
document_infos,
|
document_infos,
|
||||||
|
upload_and_parse,
|
||||||
} = api;
|
} = api;
|
||||||
|
|
||||||
const methods = {
|
const methods = {
|
||||||
@ -136,6 +137,10 @@ const methods = {
|
|||||||
url: document_delete,
|
url: document_delete,
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
},
|
},
|
||||||
|
upload_and_parse: {
|
||||||
|
url: upload_and_parse,
|
||||||
|
method: 'post',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const kbService = registerServer<keyof typeof methods>(methods, request);
|
const kbService = registerServer<keyof typeof methods>(methods, request);
|
||||||
|
@ -51,6 +51,7 @@ export default {
|
|||||||
document_upload: `${api_host}/document/upload`,
|
document_upload: `${api_host}/document/upload`,
|
||||||
web_crawl: `${api_host}/document/web_crawl`,
|
web_crawl: `${api_host}/document/web_crawl`,
|
||||||
document_infos: `${api_host}/document/infos`,
|
document_infos: `${api_host}/document/infos`,
|
||||||
|
upload_and_parse: `${api_host}/document/upload_and_parse`,
|
||||||
|
|
||||||
// chat
|
// chat
|
||||||
setDialog: `${api_host}/dialog/set`,
|
setDialog: `${api_host}/dialog/set`,
|
||||||
@ -70,6 +71,7 @@ export default {
|
|||||||
createExternalConversation: `${api_host}/api/new_conversation`,
|
createExternalConversation: `${api_host}/api/new_conversation`,
|
||||||
getExternalConversation: `${api_host}/api/conversation`,
|
getExternalConversation: `${api_host}/api/conversation`,
|
||||||
completeExternalConversation: `${api_host}/api/completion`,
|
completeExternalConversation: `${api_host}/api/completion`,
|
||||||
|
uploadAndParseExternal: `${api_host}/api/document/upload_and_parse`,
|
||||||
|
|
||||||
// file manager
|
// file manager
|
||||||
listFile: `${api_host}/file/list`,
|
listFile: `${api_host}/file/list`,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user