Feat: Upload files in the chat box #3221 (#9483)

### What problem does this PR solve?
Feat: Upload files in the chat box #3221

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2025-08-15 10:04:37 +08:00
committed by GitHub
parent 618d6bc924
commit 562349eb02
11 changed files with 233 additions and 37 deletions

View File

@ -12,7 +12,7 @@ import { PaginationProps, message } from 'antd';
import { FormInstance } from 'antd/lib';
import axios from 'axios';
import { EventSourceParserStream } from 'eventsource-parser/stream';
import { omit } from 'lodash';
import { has, isEmpty, omit } from 'lodash';
import {
ChangeEventHandler,
useCallback,
@ -166,11 +166,43 @@ export const useFetchAppConf = () => {
return appConf;
};
function useSetDoneRecord() {
const [doneRecord, setDoneRecord] = useState<Record<string, boolean>>({});
const clearDoneRecord = useCallback(() => {
setDoneRecord({});
}, []);
const setDoneRecordById = useCallback((id: string, val: boolean) => {
setDoneRecord((prev) => ({ ...prev, [id]: val }));
}, []);
const allDone = useMemo(() => {
return Object.values(doneRecord).every((val) => val);
}, [doneRecord]);
useEffect(() => {
if (!isEmpty(doneRecord) && allDone) {
clearDoneRecord();
}
}, [allDone, clearDoneRecord, doneRecord]);
return {
doneRecord,
setDoneRecord,
setDoneRecordById,
clearDoneRecord,
allDone,
};
}
export const useSendMessageWithSse = (
url: string = api.completeConversation,
) => {
const [answer, setAnswer] = useState<IAnswer>({} as IAnswer);
const [done, setDone] = useState(true);
const { doneRecord, clearDoneRecord, setDoneRecordById, allDone } =
useSetDoneRecord();
const timer = useRef<any>();
const sseRef = useRef<AbortController>();
@ -188,6 +220,17 @@ export const useSendMessageWithSse = (
}, 1000);
}, []);
const setDoneValue = useCallback(
(body: any, value: boolean) => {
if (has(body, 'chatBoxId')) {
setDoneRecordById(body.chatBoxId, value);
} else {
setDone(value);
}
},
[setDoneRecordById],
);
const send = useCallback(
async (
body: any,
@ -195,7 +238,7 @@ export const useSendMessageWithSse = (
): Promise<{ response: Response; data: ResponseType } | undefined> => {
initializeSseRef();
try {
setDone(false);
setDoneValue(body, false);
const response = await fetch(url, {
method: 'POST',
headers: {
@ -236,23 +279,34 @@ export const useSendMessageWithSse = (
}
}
}
setDone(true);
setDoneValue(body, true);
resetAnswer();
return { data: await res, response };
} catch (e) {
setDone(true);
setDoneValue(body, true);
resetAnswer();
// Swallow fetch errors silently
}
},
[initializeSseRef, url, resetAnswer],
[initializeSseRef, setDoneValue, url, resetAnswer],
);
const stopOutputMessage = useCallback(() => {
sseRef.current?.abort();
}, []);
return { send, answer, done, setDone, resetAnswer, stopOutputMessage };
return {
send,
answer,
done,
doneRecord,
allDone,
setDone,
resetAnswer,
stopOutputMessage,
clearDoneRecord,
};
};
export const useSpeechWithSse = (url: string = api.tts) => {

View File

@ -30,6 +30,7 @@ export const enum ChatApiAction {
DeleteMessage = 'deleteMessage',
FetchMindMap = 'fetchMindMap',
FetchRelatedQuestions = 'fetchRelatedQuestions',
UploadAndParse = 'upload_and_parse',
}
export const useGetChatSearchParams = () => {
@ -163,6 +164,10 @@ export const useSetDialog = () => {
queryKey: [ChatApiAction.FetchDialogList],
});
queryClient.invalidateQueries({
queryKey: [ChatApiAction.FetchDialog],
});
message.success(
t(`message.${params.dialog_id ? 'modified' : 'created'}`),
);
@ -376,6 +381,34 @@ export const useDeleteMessage = () => {
return { data, loading, deleteMessage: mutateAsync };
};
export function useUploadAndParseFile() {
const { conversationId } = useGetChatSearchParams();
const { t } = useTranslation();
const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: [ChatApiAction.UploadAndParse],
mutationFn: async (file: File) => {
const formData = new FormData();
formData.append('file', file);
formData.append('conversation_id', conversationId);
const { data } = await chatService.uploadAndParse(formData);
if (data.code === 0) {
message.success(t(`message.uploaded`));
}
return data;
},
});
return { data, loading, uploadAndParseFile: mutateAsync };
}
//#endregion
//#region search page