mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-02-07 11:05:05 +08:00
### What problem does this PR solve? Feat: Users can chat directly without first creating a conversation. #11768 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -15,12 +15,12 @@ import {
|
||||
import { MessageType } from '@/constants/chat';
|
||||
import { useScrollToBottom } from '@/hooks/logic-hooks';
|
||||
import {
|
||||
useFetchConversation,
|
||||
useFetchDialog,
|
||||
useGetChatSearchParams,
|
||||
useSetDialog,
|
||||
} from '@/hooks/use-chat-request';
|
||||
import { useFetchUserInfo } from '@/hooks/use-user-setting-request';
|
||||
import { IClientConversation, IMessage } from '@/interfaces/database/chat';
|
||||
import { buildMessageUuidWithRole } from '@/utils/chat';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { t } from 'i18next';
|
||||
@ -38,13 +38,14 @@ import { useCreateConversationBeforeUploadDocument } from '../../hooks/use-creat
|
||||
import { useSendMessage } from '../../hooks/use-send-chat-message';
|
||||
import { useSendMultipleChatMessage } from '../../hooks/use-send-multiple-message';
|
||||
import { buildMessageItemReference } from '../../utils';
|
||||
import { IMessage } from '../interface';
|
||||
import { useAddChatBox } from '../use-add-box';
|
||||
import { useSetDefaultModel } from './use-set-default-model';
|
||||
|
||||
type MultipleChatBoxProps = {
|
||||
controller: AbortController;
|
||||
chatBoxIds: string[];
|
||||
stopOutputMessage(): void;
|
||||
conversation: IClientConversation;
|
||||
} & Pick<
|
||||
ReturnType<typeof useAddChatBox>,
|
||||
'removeChatBox' | 'addChatBox' | 'chatBoxIds'
|
||||
@ -55,6 +56,7 @@ type ChatCardProps = {
|
||||
idx: number;
|
||||
derivedMessages: IMessage[];
|
||||
sendLoading: boolean;
|
||||
conversation: IClientConversation;
|
||||
} & Pick<
|
||||
MultipleChatBoxProps,
|
||||
'controller' | 'removeChatBox' | 'addChatBox' | 'chatBoxIds'
|
||||
@ -72,6 +74,7 @@ const ChatCard = forwardRef(function ChatCard(
|
||||
derivedMessages,
|
||||
sendLoading,
|
||||
clickDocumentButton,
|
||||
conversation,
|
||||
}: ChatCardProps,
|
||||
ref,
|
||||
) {
|
||||
@ -97,7 +100,8 @@ const ChatCard = forwardRef(function ChatCard(
|
||||
|
||||
const { data: userInfo } = useFetchUserInfo();
|
||||
const { data: currentDialog } = useFetchDialog();
|
||||
const { data: conversation } = useFetchConversation();
|
||||
|
||||
useSetDefaultModel(form);
|
||||
|
||||
const isLatestChat = idx === chatBoxIds.length - 1;
|
||||
|
||||
@ -202,6 +206,7 @@ export function MultipleChatBox({
|
||||
removeChatBox,
|
||||
addChatBox,
|
||||
stopOutputMessage,
|
||||
conversation,
|
||||
}: MultipleChatBoxProps) {
|
||||
const {
|
||||
value,
|
||||
@ -237,6 +242,7 @@ export function MultipleChatBox({
|
||||
ref={setFormRef(id)}
|
||||
sendLoading={sendLoading}
|
||||
clickDocumentButton={clickDocumentButton}
|
||||
conversation={conversation}
|
||||
></ChatCard>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@ -4,12 +4,13 @@ import PdfSheet from '@/components/pdf-drawer';
|
||||
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
|
||||
import { MessageType } from '@/constants/chat';
|
||||
import {
|
||||
useFetchConversation,
|
||||
useFetchDialog,
|
||||
useGetChatSearchParams,
|
||||
} from '@/hooks/use-chat-request';
|
||||
import { useFetchUserInfo } from '@/hooks/use-user-setting-request';
|
||||
import { IClientConversation } from '@/interfaces/database/chat';
|
||||
import { buildMessageUuidWithRole } from '@/utils/chat';
|
||||
import { useEffect } from 'react';
|
||||
import {
|
||||
useGetSendButtonDisabled,
|
||||
useSendButtonDisabled,
|
||||
@ -21,9 +22,14 @@ import { buildMessageItemReference } from '../../utils';
|
||||
interface IProps {
|
||||
controller: AbortController;
|
||||
stopOutputMessage(): void;
|
||||
conversation: IClientConversation;
|
||||
}
|
||||
|
||||
export function SingleChatBox({ controller, stopOutputMessage }: IProps) {
|
||||
export function SingleChatBox({
|
||||
controller,
|
||||
stopOutputMessage,
|
||||
conversation,
|
||||
}: IProps) {
|
||||
const {
|
||||
value,
|
||||
scrollRef,
|
||||
@ -37,18 +43,32 @@ export function SingleChatBox({ controller, stopOutputMessage }: IProps) {
|
||||
removeMessageById,
|
||||
handleUploadFile,
|
||||
removeFile,
|
||||
setDerivedMessages,
|
||||
} = useSendMessage(controller);
|
||||
const { data: userInfo } = useFetchUserInfo();
|
||||
const { data: currentDialog } = useFetchDialog();
|
||||
const { createConversationBeforeUploadDocument } =
|
||||
useCreateConversationBeforeUploadDocument();
|
||||
const { conversationId } = useGetChatSearchParams();
|
||||
const { data: conversation } = useFetchConversation();
|
||||
const disabled = useGetSendButtonDisabled();
|
||||
const sendDisabled = useSendButtonDisabled(value);
|
||||
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
||||
useClickDrawer();
|
||||
|
||||
useEffect(() => {
|
||||
const messages = conversation?.message;
|
||||
if (Array.isArray(messages)) {
|
||||
setDerivedMessages(messages);
|
||||
}
|
||||
}, [conversation?.message, setDerivedMessages]);
|
||||
|
||||
useEffect(() => {
|
||||
// Clear the message list after deleting the conversation.
|
||||
if (conversationId === '') {
|
||||
setDerivedMessages([]);
|
||||
}
|
||||
}, [conversationId, setDerivedMessages]);
|
||||
|
||||
return (
|
||||
<section className="flex flex-col p-5 h-full">
|
||||
<div ref={messageContainerRef} className="flex-1 overflow-auto min-h-0">
|
||||
|
||||
@ -0,0 +1,18 @@
|
||||
import { LlmModelType } from '@/constants/knowledge';
|
||||
import { useComposeLlmOptionsByModelTypes } from '@/hooks/use-llm-request';
|
||||
import { useMount } from 'ahooks';
|
||||
import { UseFormReturn } from 'react-hook-form';
|
||||
|
||||
export function useSetDefaultModel(form: UseFormReturn<any>) {
|
||||
const modelOptions = useComposeLlmOptionsByModelTypes([
|
||||
LlmModelType.Chat,
|
||||
LlmModelType.Image2text,
|
||||
]);
|
||||
|
||||
useMount(() => {
|
||||
const firstModel = modelOptions.at(0)?.options.at(0)?.value;
|
||||
if (firstModel) {
|
||||
form.setValue('llm_id', firstModel);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -5,11 +5,15 @@ import {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { useRemoveConversation } from '@/hooks/use-chat-request';
|
||||
import {
|
||||
useGetChatSearchParams,
|
||||
useRemoveConversation,
|
||||
} from '@/hooks/use-chat-request';
|
||||
import { IConversation } from '@/interfaces/database/chat';
|
||||
import { Trash2 } from 'lucide-react';
|
||||
import { MouseEventHandler, PropsWithChildren, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useChatUrlParams } from '../hooks/use-chat-url';
|
||||
|
||||
export function ConversationDropdown({
|
||||
children,
|
||||
@ -20,22 +24,27 @@ export function ConversationDropdown({
|
||||
removeTemporaryConversation?: (conversationId: string) => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { setConversationBoth } = useChatUrlParams();
|
||||
const { removeConversation } = useRemoveConversation();
|
||||
const { isNew } = useGetChatSearchParams();
|
||||
|
||||
const handleDelete: MouseEventHandler<HTMLDivElement> = useCallback(() => {
|
||||
if (conversation.is_new && removeTemporaryConversation) {
|
||||
removeTemporaryConversation(conversation.id);
|
||||
removeConversation([]);
|
||||
} else {
|
||||
removeConversation([conversation.id]);
|
||||
}
|
||||
}, [
|
||||
conversation.id,
|
||||
conversation.is_new,
|
||||
removeConversation,
|
||||
removeTemporaryConversation,
|
||||
]);
|
||||
const handleDelete: MouseEventHandler<HTMLDivElement> =
|
||||
useCallback(async () => {
|
||||
if (isNew === 'true' && removeTemporaryConversation) {
|
||||
removeTemporaryConversation(conversation.id);
|
||||
} else {
|
||||
const code = await removeConversation([conversation.id]);
|
||||
if (code === 0) {
|
||||
setConversationBoth('', '');
|
||||
}
|
||||
}
|
||||
}, [
|
||||
conversation.id,
|
||||
isNew,
|
||||
removeConversation,
|
||||
removeTemporaryConversation,
|
||||
setConversationBoth,
|
||||
]);
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
|
||||
@ -15,13 +15,17 @@ import { SharedFrom } from '@/constants/chat';
|
||||
import { useSetModalState } from '@/hooks/common-hooks';
|
||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||
import {
|
||||
useFetchConversation,
|
||||
useFetchConversationList,
|
||||
useFetchConversationManually,
|
||||
useFetchDialog,
|
||||
useGetChatSearchParams,
|
||||
} from '@/hooks/use-chat-request';
|
||||
import { IClientConversation } from '@/interfaces/database/chat';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useMount } from 'ahooks';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { ArrowUpRight, LogOut, Send } from 'lucide-react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useParams } from 'umi';
|
||||
import { useHandleClickConversationCard } from '../hooks/use-click-card';
|
||||
@ -37,26 +41,54 @@ export default function Chat() {
|
||||
const { navigateToChatList } = useNavigatePage();
|
||||
const { data } = useFetchDialog();
|
||||
const { t } = useTranslation();
|
||||
const { data: conversation } = useFetchConversation();
|
||||
const [currentConversation, setCurrentConversation] =
|
||||
useState<IClientConversation>({} as IClientConversation);
|
||||
|
||||
const { fetchConversationManually } = useFetchConversationManually();
|
||||
|
||||
const { handleConversationCardClick, controller, stopOutputMessage } =
|
||||
useHandleClickConversationCard();
|
||||
const { visible: settingVisible, switchVisible: switchSettingVisible } =
|
||||
useSetModalState(true);
|
||||
const {
|
||||
removeChatBox,
|
||||
addChatBox,
|
||||
chatBoxIds,
|
||||
hasSingleChatBox,
|
||||
hasThreeChatBox,
|
||||
} = useAddChatBox();
|
||||
|
||||
const { isDebugMode, switchDebugMode } = useSwitchDebugMode();
|
||||
const { removeChatBox, addChatBox, chatBoxIds, hasSingleChatBox } =
|
||||
useAddChatBox(isDebugMode);
|
||||
|
||||
const { showEmbedModal, hideEmbedModal, embedVisible, beta } =
|
||||
useShowEmbedModal();
|
||||
|
||||
const { conversationId, isNew } = useGetChatSearchParams();
|
||||
|
||||
const { isDebugMode, switchDebugMode } = useSwitchDebugMode();
|
||||
const { data: dialogList } = useFetchConversationList();
|
||||
|
||||
const currentConversationName = useMemo(() => {
|
||||
return dialogList.find((x) => x.id === conversationId)?.name;
|
||||
}, [conversationId, dialogList]);
|
||||
|
||||
const fetchConversation: typeof handleConversationCardClick = useCallback(
|
||||
async (conversationId, isNew) => {
|
||||
if (conversationId && !isNew) {
|
||||
const conversation = await fetchConversationManually(conversationId);
|
||||
if (!isEmpty(conversation)) {
|
||||
setCurrentConversation(conversation);
|
||||
}
|
||||
}
|
||||
},
|
||||
[fetchConversationManually],
|
||||
);
|
||||
|
||||
const handleSessionClick: typeof handleConversationCardClick = useCallback(
|
||||
(conversationId, isNew) => {
|
||||
handleConversationCardClick(conversationId, isNew);
|
||||
fetchConversation(conversationId, isNew);
|
||||
},
|
||||
[fetchConversation, handleConversationCardClick],
|
||||
);
|
||||
|
||||
useMount(() => {
|
||||
fetchConversation(conversationId, isNew === 'true');
|
||||
});
|
||||
|
||||
if (isDebugMode) {
|
||||
return (
|
||||
@ -75,6 +107,7 @@ export default function Chat() {
|
||||
removeChatBox={removeChatBox}
|
||||
addChatBox={addChatBox}
|
||||
stopOutputMessage={stopOutputMessage}
|
||||
conversation={currentConversation}
|
||||
></MultipleChatBox>
|
||||
</section>
|
||||
);
|
||||
@ -104,7 +137,7 @@ export default function Chat() {
|
||||
<div className="flex flex-1 min-h-0 pb-9">
|
||||
<Sessions
|
||||
hasSingleChatBox={hasSingleChatBox}
|
||||
handleConversationCardClick={handleConversationCardClick}
|
||||
handleConversationCardClick={handleSessionClick}
|
||||
switchSettingVisible={switchSettingVisible}
|
||||
></Sessions>
|
||||
|
||||
@ -115,16 +148,8 @@ export default function Chat() {
|
||||
className={cn('p-5', { 'border-b': hasSingleChatBox })}
|
||||
>
|
||||
<CardTitle className="flex justify-between items-center text-base">
|
||||
<div className="truncate">{conversation.name}</div>
|
||||
<Button
|
||||
variant={'ghost'}
|
||||
onClick={switchDebugMode}
|
||||
disabled={
|
||||
hasThreeChatBox ||
|
||||
isEmpty(conversationId) ||
|
||||
isNew === 'true'
|
||||
}
|
||||
>
|
||||
<div className="truncate">{currentConversationName}</div>
|
||||
<Button variant={'ghost'} onClick={switchDebugMode}>
|
||||
<ArrowUpRight /> {t('chat.multipleModels')}
|
||||
</Button>
|
||||
</CardTitle>
|
||||
@ -133,6 +158,7 @@ export default function Chat() {
|
||||
<SingleChatBox
|
||||
controller={controller}
|
||||
stopOutputMessage={stopOutputMessage}
|
||||
conversation={currentConversation}
|
||||
></SingleChatBox>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
export function useAddChatBox() {
|
||||
export function useAddChatBox(isDebugMode: boolean) {
|
||||
const [ids, setIds] = useState<string[]>([uuid()]);
|
||||
|
||||
const hasSingleChatBox = ids.length === 1;
|
||||
@ -16,6 +16,12 @@ export function useAddChatBox() {
|
||||
setIds((prev) => prev.filter((x) => x !== id));
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isDebugMode) {
|
||||
setIds((pre) => pre.slice(0, 1));
|
||||
}
|
||||
}, [isDebugMode]);
|
||||
|
||||
return {
|
||||
chatBoxIds: ids,
|
||||
hasSingleChatBox,
|
||||
|
||||
Reference in New Issue
Block a user