mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
feat: render message reference and add avatar to MessageItem (#73)
* feat: add temporary conversation * feat: add avatar to MessageItem * feat: render message reference
This commit is contained in:
25
web/src/assets/svg/assistant.svg
Normal file
25
web/src/assets/svg/assistant.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 30 KiB |
22
web/src/hooks/userSettingHook.ts
Normal file
22
web/src/hooks/userSettingHook.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { IUserInfo } from '@/interfaces/database/userSetting';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'umi';
|
||||
|
||||
export const useFetchUserInfo = () => {
|
||||
const dispatch = useDispatch();
|
||||
const fetchUserInfo = useCallback(() => {
|
||||
dispatch({ type: 'settingModel/getUserInfo' });
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchUserInfo();
|
||||
}, [fetchUserInfo]);
|
||||
};
|
||||
|
||||
export const useSelectUserInfo = () => {
|
||||
const userInfo: IUserInfo = useSelector(
|
||||
(state: any) => state.settingModel.userInfo,
|
||||
);
|
||||
|
||||
return userInfo;
|
||||
};
|
||||
@ -54,7 +54,7 @@ export interface IConversation {
|
||||
dialog_id: string;
|
||||
id: string;
|
||||
message: Message[];
|
||||
reference: any[];
|
||||
reference: IReference[];
|
||||
name: string;
|
||||
update_date: string;
|
||||
update_time: number;
|
||||
@ -64,3 +64,29 @@ export interface Message {
|
||||
content: string;
|
||||
role: MessageType;
|
||||
}
|
||||
|
||||
export interface IReference {
|
||||
chunks: Chunk[];
|
||||
doc_aggs: Docagg[];
|
||||
total: number;
|
||||
}
|
||||
|
||||
interface Docagg {
|
||||
count: number;
|
||||
doc_id: string;
|
||||
doc_name: string;
|
||||
}
|
||||
|
||||
interface Chunk {
|
||||
chunk_id: string;
|
||||
content_ltks: string;
|
||||
content_with_weight: string;
|
||||
doc_id: string;
|
||||
docnm_kwd: string;
|
||||
img_id: string;
|
||||
important_kwd: any[];
|
||||
kb_id: string;
|
||||
similarity: number;
|
||||
term_similarity: number;
|
||||
vector_similarity: number;
|
||||
}
|
||||
|
||||
21
web/src/interfaces/database/userSetting.ts
Normal file
21
web/src/interfaces/database/userSetting.ts
Normal file
@ -0,0 +1,21 @@
|
||||
export interface IUserInfo {
|
||||
access_token: string;
|
||||
avatar?: any;
|
||||
color_schema: string;
|
||||
create_date: string;
|
||||
create_time: number;
|
||||
email: string;
|
||||
id: string;
|
||||
is_active: string;
|
||||
is_anonymous: string;
|
||||
is_authenticated: string;
|
||||
is_superuser: boolean;
|
||||
language: string;
|
||||
last_login_time: string;
|
||||
login_channel: string;
|
||||
nickname: string;
|
||||
password: string;
|
||||
status: string;
|
||||
update_date: string;
|
||||
update_time: number;
|
||||
}
|
||||
@ -19,17 +19,20 @@ const RagHeader = () => {
|
||||
const navigate = useNavigate();
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const tagsData = [
|
||||
{ path: '/knowledge', name: 'Knowledge Base', icon: KnowledgeBaseIcon },
|
||||
{ path: '/chat', name: 'Chat', icon: StarIon },
|
||||
{ path: '/file', name: 'File Management', icon: FileIcon },
|
||||
];
|
||||
const tagsData = useMemo(
|
||||
() => [
|
||||
{ path: '/knowledge', name: 'Knowledge Base', icon: KnowledgeBaseIcon },
|
||||
{ path: '/chat', name: 'Chat', icon: StarIon },
|
||||
{ path: '/file', name: 'File Management', icon: FileIcon },
|
||||
],
|
||||
[],
|
||||
);
|
||||
|
||||
const currentPath = useMemo(() => {
|
||||
return (
|
||||
tagsData.find((x) => pathname.startsWith(x.path))?.name || 'knowledge'
|
||||
);
|
||||
}, [pathname]);
|
||||
}, [pathname, tagsData]);
|
||||
|
||||
const handleChange = (path: string) => {
|
||||
navigate(path);
|
||||
@ -48,7 +51,7 @@ const RagHeader = () => {
|
||||
>
|
||||
<Space size={12}>
|
||||
<Logo className={styles.appIcon}></Logo>
|
||||
<label className={styles.appName}>Infinity flow</label>
|
||||
<label className={styles.appName}>RagFlow</label>
|
||||
</Space>
|
||||
<Space size={[0, 8]} wrap>
|
||||
<Radio.Group
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { useFetchUserInfo, useSelectUserInfo } from '@/hooks/userSettingHook';
|
||||
import authorizationUtil from '@/utils/authorizationUtil';
|
||||
import type { MenuProps } from 'antd';
|
||||
import { Avatar, Button, Dropdown } from 'antd';
|
||||
@ -7,6 +8,7 @@ import { history } from 'umi';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const userInfo = useSelectUserInfo();
|
||||
|
||||
const logout = () => {
|
||||
authorizationUtil.removeAll();
|
||||
@ -36,13 +38,18 @@ const App: React.FC = () => {
|
||||
),
|
||||
},
|
||||
];
|
||||
}, []);
|
||||
}, [t]);
|
||||
|
||||
useFetchUserInfo();
|
||||
|
||||
return (
|
||||
<Dropdown menu={{ items }} placement="bottomLeft" arrow>
|
||||
<Avatar
|
||||
size={32}
|
||||
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
|
||||
src={
|
||||
userInfo.avatar ??
|
||||
'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png'
|
||||
}
|
||||
/>
|
||||
</Dropdown>
|
||||
);
|
||||
|
||||
@ -1,11 +1,34 @@
|
||||
.chatContainer {
|
||||
padding: 0 24px 24px;
|
||||
.messageContainer {
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.messageItem {
|
||||
.messageItemContent {
|
||||
padding: 24px 0;
|
||||
.messageItemSection {
|
||||
display: inline-block;
|
||||
width: 300px;
|
||||
}
|
||||
.messageItemSectionLeft {
|
||||
width: 70%;
|
||||
}
|
||||
.messageItemSectionRight {
|
||||
width: 30%;
|
||||
}
|
||||
.messageItemContent {
|
||||
display: inline-flex;
|
||||
gap: 20px;
|
||||
}
|
||||
.messageItemContentReverse {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
.messageText {
|
||||
padding: 0 14px;
|
||||
background-color: rgba(249, 250, 251, 1);
|
||||
}
|
||||
.referenceIcon {
|
||||
padding: 0 6px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,17 +1,59 @@
|
||||
import { Button, Flex, Input, Typography } from 'antd';
|
||||
import { ChangeEventHandler, useState } from 'react';
|
||||
|
||||
import { Message } from '@/interfaces/database/chat';
|
||||
import classNames from 'classnames';
|
||||
import { useFetchConversation, useSendMessage } from '../hooks';
|
||||
|
||||
import { ReactComponent as AssistantIcon } from '@/assets/svg/assistant.svg';
|
||||
import { MessageType } from '@/constants/chat';
|
||||
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
||||
import { useSelectUserInfo } from '@/hooks/userSettingHook';
|
||||
import { IReference, Message } from '@/interfaces/database/chat';
|
||||
import { Avatar, Button, Flex, Input, Popover } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import { ChangeEventHandler, useCallback, useMemo, useState } from 'react';
|
||||
import reactStringReplace from 'react-string-replace';
|
||||
import { useFetchConversation, useSendMessage } from '../hooks';
|
||||
import { IClientConversation } from '../interface';
|
||||
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import Markdown from 'react-markdown';
|
||||
import { visitParents } from 'unist-util-visit-parents';
|
||||
import styles from './index.less';
|
||||
|
||||
const { Paragraph } = Typography;
|
||||
const rehypeWrapReference = () => {
|
||||
return function wrapTextTransform(tree: any) {
|
||||
visitParents(tree, 'text', (node, ancestors) => {
|
||||
if (ancestors.at(-1).tagName !== 'custom-typography') {
|
||||
node.type = 'element';
|
||||
node.tagName = 'custom-typography';
|
||||
node.properties = {};
|
||||
node.children = [{ type: 'text', value: node.value }];
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const MessageItem = ({ item }: { item: Message; references: IReference[] }) => {
|
||||
const userInfo = useSelectUserInfo();
|
||||
|
||||
const popoverContent = useMemo(
|
||||
() => (
|
||||
<div>
|
||||
<p>Content</p>
|
||||
<p>Content</p>
|
||||
</div>
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
const renderReference = useCallback(
|
||||
(text: string) => {
|
||||
return reactStringReplace(text, /#{2}\d{1,}\${2}/g, (match, i) => {
|
||||
return (
|
||||
<Popover content={popoverContent}>
|
||||
<InfoCircleOutlined key={i} className={styles.referenceIcon} />
|
||||
</Popover>
|
||||
);
|
||||
});
|
||||
},
|
||||
[popoverContent],
|
||||
);
|
||||
|
||||
const MessageItem = ({ item }: { item: Message }) => {
|
||||
return (
|
||||
<div
|
||||
className={classNames(styles.messageItem, {
|
||||
@ -19,11 +61,50 @@ const MessageItem = ({ item }: { item: Message }) => {
|
||||
[styles.messageItemRight]: item.role === MessageType.User,
|
||||
})}
|
||||
>
|
||||
<span className={styles.messageItemContent}>
|
||||
<Paragraph ellipsis={{ tooltip: item.content, rows: 3 }}>
|
||||
{item.content}
|
||||
</Paragraph>
|
||||
</span>
|
||||
<section
|
||||
className={classNames(styles.messageItemSection, {
|
||||
[styles.messageItemSectionLeft]: item.role === MessageType.Assistant,
|
||||
[styles.messageItemSectionRight]: item.role === MessageType.User,
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className={classNames(styles.messageItemContent, {
|
||||
[styles.messageItemContentReverse]: item.role === MessageType.User,
|
||||
})}
|
||||
>
|
||||
{item.role === MessageType.User ? (
|
||||
userInfo.avatar ?? (
|
||||
<Avatar
|
||||
size={40}
|
||||
src={
|
||||
userInfo.avatar ??
|
||||
'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png'
|
||||
}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
<AssistantIcon></AssistantIcon>
|
||||
)}
|
||||
<Flex vertical gap={8} flex={1}>
|
||||
<b>
|
||||
{item.role === MessageType.Assistant ? 'Resume Assistant' : 'You'}
|
||||
</b>
|
||||
<div className={styles.messageText}>
|
||||
<Markdown
|
||||
rehypePlugins={[rehypeWrapReference]}
|
||||
components={
|
||||
{
|
||||
'custom-typography': ({ children }: { children: string }) =>
|
||||
renderReference(children),
|
||||
} as any
|
||||
}
|
||||
>
|
||||
{item.content}
|
||||
</Markdown>
|
||||
</div>
|
||||
</Flex>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -32,9 +113,13 @@ const ChatContainer = () => {
|
||||
const [value, setValue] = useState('');
|
||||
const conversation: IClientConversation = useFetchConversation();
|
||||
const { sendMessage } = useSendMessage();
|
||||
const loading = useOneNamespaceEffectsLoading('chatModel', [
|
||||
'completeConversation',
|
||||
'getConversation',
|
||||
]);
|
||||
|
||||
const handlePressEnter = () => {
|
||||
console.info(value);
|
||||
setValue('');
|
||||
sendMessage(value);
|
||||
};
|
||||
|
||||
@ -44,17 +129,23 @@ const ChatContainer = () => {
|
||||
|
||||
return (
|
||||
<Flex flex={1} className={styles.chatContainer} vertical>
|
||||
<Flex flex={1} vertical>
|
||||
{conversation?.message?.map((message) => (
|
||||
<MessageItem key={message.id} item={message}></MessageItem>
|
||||
))}
|
||||
<Flex flex={1} vertical className={styles.messageContainer}>
|
||||
<div>
|
||||
{conversation?.message?.map((message) => (
|
||||
<MessageItem
|
||||
key={message.id}
|
||||
item={message}
|
||||
references={conversation.reference}
|
||||
></MessageItem>
|
||||
))}
|
||||
</div>
|
||||
</Flex>
|
||||
<Input
|
||||
size="large"
|
||||
placeholder="Message Resume Assistant..."
|
||||
value={value}
|
||||
suffix={
|
||||
<Button type="primary" onClick={handlePressEnter}>
|
||||
<Button type="primary" onClick={handlePressEnter} loading={loading}>
|
||||
Send
|
||||
</Button>
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import showDeleteConfirm from '@/components/deleting-confirm';
|
||||
import { MessageType } from '@/constants/chat';
|
||||
import { IDialog } from '@/interfaces/database/chat';
|
||||
import { IConversation, IDialog } from '@/interfaces/database/chat';
|
||||
import omit from 'lodash/omit';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useDispatch, useSearchParams, useSelector } from 'umi';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { ChatSearchParams, EmptyConversationId } from './constants';
|
||||
@ -11,6 +11,8 @@ import {
|
||||
IMessage,
|
||||
VariableTableDataType,
|
||||
} from './interface';
|
||||
import { ChatModelState } from './model';
|
||||
import { isConversationIdNotExist } from './utils';
|
||||
|
||||
export const useFetchDialogList = () => {
|
||||
const dispatch = useDispatch();
|
||||
@ -137,24 +139,6 @@ export const useRemoveDialog = () => {
|
||||
return { onRemoveDialog };
|
||||
};
|
||||
|
||||
export const useClickDialogCard = () => {
|
||||
const [currentQueryParameters, setSearchParams] = useSearchParams();
|
||||
|
||||
const newQueryParameters: URLSearchParams = useMemo(() => {
|
||||
return new URLSearchParams(currentQueryParameters.toString());
|
||||
}, [currentQueryParameters]);
|
||||
|
||||
const handleClickDialog = useCallback(
|
||||
(dialogId: string) => {
|
||||
newQueryParameters.set(ChatSearchParams.DialogId, dialogId);
|
||||
setSearchParams(newQueryParameters);
|
||||
},
|
||||
[newQueryParameters, setSearchParams],
|
||||
);
|
||||
|
||||
return { handleClickDialog };
|
||||
};
|
||||
|
||||
export const useGetChatSearchParams = () => {
|
||||
const [currentQueryParameters] = useSearchParams();
|
||||
|
||||
@ -165,6 +149,44 @@ export const useGetChatSearchParams = () => {
|
||||
};
|
||||
};
|
||||
|
||||
export const useSetCurrentConversation = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const setCurrentConversation = useCallback(
|
||||
(currentConversation: IClientConversation) => {
|
||||
dispatch({
|
||||
type: 'chatModel/setCurrentConversation',
|
||||
payload: currentConversation,
|
||||
});
|
||||
},
|
||||
[dispatch],
|
||||
);
|
||||
|
||||
return setCurrentConversation;
|
||||
};
|
||||
|
||||
export const useClickDialogCard = () => {
|
||||
const [currentQueryParameters, setSearchParams] = useSearchParams();
|
||||
|
||||
const newQueryParameters: URLSearchParams = useMemo(() => {
|
||||
return new URLSearchParams();
|
||||
}, []);
|
||||
|
||||
const handleClickDialog = useCallback(
|
||||
(dialogId: string) => {
|
||||
newQueryParameters.set(ChatSearchParams.DialogId, dialogId);
|
||||
// newQueryParameters.set(
|
||||
// ChatSearchParams.ConversationId,
|
||||
// EmptyConversationId,
|
||||
// );
|
||||
setSearchParams(newQueryParameters);
|
||||
},
|
||||
[newQueryParameters, setSearchParams],
|
||||
);
|
||||
|
||||
return { handleClickDialog };
|
||||
};
|
||||
|
||||
export const useSelectFirstDialogOnMount = () => {
|
||||
const dialogList = useFetchDialogList();
|
||||
const { dialogId } = useGetChatSearchParams();
|
||||
@ -182,13 +204,83 @@ export const useSelectFirstDialogOnMount = () => {
|
||||
|
||||
//#region conversation
|
||||
|
||||
export const useFetchConversationList = (dialogId?: string) => {
|
||||
export const useCreateTemporaryConversation = () => {
|
||||
const dispatch = useDispatch();
|
||||
const { dialogId } = useGetChatSearchParams();
|
||||
const { handleClickConversation } = useClickConversationCard();
|
||||
let chatModel = useSelector((state: any) => state.chatModel);
|
||||
|
||||
const currentConversation: Pick<
|
||||
IClientConversation,
|
||||
'id' | 'message' | 'name' | 'dialog_id'
|
||||
> = chatModel.currentConversation;
|
||||
|
||||
const conversationList: IClientConversation[] = chatModel.conversationList;
|
||||
const currentDialog: IDialog = chatModel.currentDialog;
|
||||
|
||||
const setCurrentConversation = useSetCurrentConversation();
|
||||
|
||||
const createTemporaryConversation = useCallback(() => {
|
||||
const firstConversation = conversationList[0];
|
||||
const messages = [...(firstConversation?.message ?? [])];
|
||||
if (messages.some((x) => x.id === EmptyConversationId)) {
|
||||
return;
|
||||
}
|
||||
messages.push({
|
||||
id: EmptyConversationId,
|
||||
content: currentDialog?.prompt_config?.prologue ?? '',
|
||||
role: MessageType.Assistant,
|
||||
});
|
||||
|
||||
let nextCurrentConversation = currentConversation;
|
||||
|
||||
// It’s the back-end data.
|
||||
if ('id' in currentConversation) {
|
||||
nextCurrentConversation = { ...currentConversation, message: messages };
|
||||
} else {
|
||||
// client data
|
||||
nextCurrentConversation = {
|
||||
id: EmptyConversationId,
|
||||
name: 'New conversation',
|
||||
dialog_id: dialogId,
|
||||
message: messages,
|
||||
};
|
||||
}
|
||||
|
||||
const nextConversationList = [...conversationList];
|
||||
|
||||
nextConversationList.unshift(
|
||||
nextCurrentConversation as IClientConversation,
|
||||
);
|
||||
|
||||
setCurrentConversation(nextCurrentConversation as IClientConversation);
|
||||
|
||||
dispatch({
|
||||
type: 'chatModel/setConversationList',
|
||||
payload: nextConversationList,
|
||||
});
|
||||
handleClickConversation(EmptyConversationId);
|
||||
}, [
|
||||
dispatch,
|
||||
currentConversation,
|
||||
dialogId,
|
||||
setCurrentConversation,
|
||||
handleClickConversation,
|
||||
conversationList,
|
||||
currentDialog,
|
||||
]);
|
||||
|
||||
return { createTemporaryConversation };
|
||||
};
|
||||
|
||||
export const useFetchConversationList = () => {
|
||||
const dispatch = useDispatch();
|
||||
const conversationList: any[] = useSelector(
|
||||
(state: any) => state.chatModel.conversationList,
|
||||
);
|
||||
const { dialogId } = useGetChatSearchParams();
|
||||
|
||||
const fetchConversationList = useCallback(() => {
|
||||
const fetchConversationList = useCallback(async () => {
|
||||
if (dialogId) {
|
||||
dispatch({
|
||||
type: 'chatModel/listConversation',
|
||||
@ -204,72 +296,56 @@ export const useFetchConversationList = (dialogId?: string) => {
|
||||
return conversationList;
|
||||
};
|
||||
|
||||
export const useClickConversationCard = () => {
|
||||
const [currentQueryParameters, setSearchParams] = useSearchParams();
|
||||
const newQueryParameters: URLSearchParams = new URLSearchParams(
|
||||
currentQueryParameters.toString(),
|
||||
);
|
||||
export const useSelectConversationList = () => {
|
||||
const [list, setList] = useState<Array<IConversation>>([]);
|
||||
let chatModel: ChatModelState = useSelector((state: any) => state.chatModel);
|
||||
const { conversationList, currentDialog } = chatModel;
|
||||
const { dialogId } = useGetChatSearchParams();
|
||||
const prologue = currentDialog?.prompt_config?.prologue ?? '';
|
||||
|
||||
const handleClickConversation = (conversationId: string) => {
|
||||
newQueryParameters.set(ChatSearchParams.ConversationId, conversationId);
|
||||
setSearchParams(newQueryParameters);
|
||||
};
|
||||
const addTemporaryConversation = useCallback(() => {
|
||||
setList(() => {
|
||||
const nextList = [
|
||||
{
|
||||
id: '',
|
||||
name: 'New conversation',
|
||||
dialog_id: dialogId,
|
||||
message: [
|
||||
{
|
||||
content: prologue,
|
||||
role: MessageType.Assistant,
|
||||
},
|
||||
],
|
||||
} as IConversation,
|
||||
...conversationList,
|
||||
];
|
||||
return nextList;
|
||||
});
|
||||
}, [conversationList, dialogId, prologue]);
|
||||
|
||||
return { handleClickConversation };
|
||||
useEffect(() => {
|
||||
addTemporaryConversation();
|
||||
}, [addTemporaryConversation]);
|
||||
|
||||
return { list, addTemporaryConversation };
|
||||
};
|
||||
|
||||
export const useCreateTemporaryConversation = () => {
|
||||
const dispatch = useDispatch();
|
||||
const { dialogId } = useGetChatSearchParams();
|
||||
const { handleClickConversation } = useClickConversationCard();
|
||||
let chatModel = useSelector((state: any) => state.chatModel);
|
||||
let currentConversation: Pick<
|
||||
IClientConversation,
|
||||
'id' | 'message' | 'name' | 'dialog_id'
|
||||
> = chatModel.currentConversation;
|
||||
let conversationList: IClientConversation[] = chatModel.conversationList;
|
||||
export const useClickConversationCard = () => {
|
||||
const [currentQueryParameters, setSearchParams] = useSearchParams();
|
||||
const newQueryParameters: URLSearchParams = useMemo(
|
||||
() => new URLSearchParams(currentQueryParameters.toString()),
|
||||
[currentQueryParameters],
|
||||
);
|
||||
|
||||
const createTemporaryConversation = (message: string) => {
|
||||
const messages = [...(currentConversation?.message ?? [])];
|
||||
if (messages.some((x) => x.id === EmptyConversationId)) {
|
||||
return;
|
||||
}
|
||||
messages.unshift({
|
||||
id: EmptyConversationId,
|
||||
content: message,
|
||||
role: MessageType.Assistant,
|
||||
});
|
||||
const handleClickConversation = useCallback(
|
||||
(conversationId: string) => {
|
||||
newQueryParameters.set(ChatSearchParams.ConversationId, conversationId);
|
||||
setSearchParams(newQueryParameters);
|
||||
},
|
||||
[newQueryParameters, setSearchParams],
|
||||
);
|
||||
|
||||
// It’s the back-end data.
|
||||
if ('id' in currentConversation) {
|
||||
currentConversation = { ...currentConversation, message: messages };
|
||||
} else {
|
||||
// client data
|
||||
currentConversation = {
|
||||
id: EmptyConversationId,
|
||||
name: 'New conversation',
|
||||
dialog_id: dialogId,
|
||||
message: messages,
|
||||
};
|
||||
}
|
||||
|
||||
const nextConversationList = [...conversationList];
|
||||
|
||||
nextConversationList.push(currentConversation as IClientConversation);
|
||||
|
||||
dispatch({
|
||||
type: 'chatModel/setCurrentConversation',
|
||||
payload: currentConversation,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: 'chatModel/setConversationList',
|
||||
payload: nextConversationList,
|
||||
});
|
||||
handleClickConversation(EmptyConversationId);
|
||||
};
|
||||
|
||||
return { createTemporaryConversation };
|
||||
return { handleClickConversation };
|
||||
};
|
||||
|
||||
export const useSetConversation = () => {
|
||||
@ -302,17 +378,20 @@ export const useFetchConversation = () => {
|
||||
const conversation = useSelector(
|
||||
(state: any) => state.chatModel.currentConversation,
|
||||
);
|
||||
const setCurrentConversation = useSetCurrentConversation();
|
||||
|
||||
const fetchConversation = useCallback(() => {
|
||||
if (conversationId !== EmptyConversationId && conversationId !== '') {
|
||||
dispatch({
|
||||
if (isConversationIdNotExist(conversationId)) {
|
||||
dispatch<any>({
|
||||
type: 'chatModel/getConversation',
|
||||
payload: {
|
||||
conversation_id: conversationId,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
setCurrentConversation({} as IClientConversation);
|
||||
}
|
||||
}, [dispatch, conversationId]);
|
||||
}, [dispatch, conversationId, setCurrentConversation]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchConversation();
|
||||
@ -347,7 +426,7 @@ export const useSendMessage = () => {
|
||||
};
|
||||
|
||||
const handleSendMessage = async (message: string) => {
|
||||
if (conversationId !== EmptyConversationId) {
|
||||
if (conversationId !== '') {
|
||||
sendMessage(message);
|
||||
} else {
|
||||
const data = await setConversation(message);
|
||||
|
||||
@ -18,11 +18,11 @@ import ChatContainer from './chat-container';
|
||||
import {
|
||||
useClickConversationCard,
|
||||
useClickDialogCard,
|
||||
useCreateTemporaryConversation,
|
||||
useFetchConversationList,
|
||||
useFetchDialog,
|
||||
useGetChatSearchParams,
|
||||
useRemoveDialog,
|
||||
useSelectConversationList,
|
||||
useSelectFirstDialogOnMount,
|
||||
useSetCurrentDialog,
|
||||
} from './hooks';
|
||||
@ -38,12 +38,10 @@ const Chat = () => {
|
||||
const { handleClickDialog } = useClickDialogCard();
|
||||
const { handleClickConversation } = useClickConversationCard();
|
||||
const { dialogId, conversationId } = useGetChatSearchParams();
|
||||
const list = useFetchConversationList(dialogId);
|
||||
const { createTemporaryConversation } = useCreateTemporaryConversation();
|
||||
const { list: conversationList, addTemporaryConversation } =
|
||||
useSelectConversationList();
|
||||
|
||||
const selectedDialog = useFetchDialog(dialogId, true);
|
||||
|
||||
const prologue = selectedDialog?.prompt_config?.prologue || '';
|
||||
useFetchDialog(dialogId, true);
|
||||
|
||||
const handleAppCardEnter = (id: string) => () => {
|
||||
setActivated(id);
|
||||
@ -69,8 +67,8 @@ const Chat = () => {
|
||||
};
|
||||
|
||||
const handleCreateTemporaryConversation = useCallback(() => {
|
||||
createTemporaryConversation(prologue);
|
||||
}, [createTemporaryConversation, prologue]);
|
||||
addTemporaryConversation();
|
||||
}, [addTemporaryConversation]);
|
||||
|
||||
const items: MenuProps['items'] = [
|
||||
{
|
||||
@ -112,6 +110,8 @@ const Chat = () => {
|
||||
return appItems;
|
||||
};
|
||||
|
||||
useFetchConversationList();
|
||||
|
||||
return (
|
||||
<Flex className={styles.chatWrapper}>
|
||||
<Flex className={styles.chatAppWrapper}>
|
||||
@ -171,7 +171,7 @@ const Chat = () => {
|
||||
</Flex>
|
||||
<Divider></Divider>
|
||||
<Flex vertical gap={10} className={styles.chatTitleContent}>
|
||||
{list.map((x) => (
|
||||
{conversationList.map((x) => (
|
||||
<Card
|
||||
key={x.id}
|
||||
hoverable
|
||||
|
||||
@ -48,10 +48,11 @@ const model: DvaModel<ChatModelState> = {
|
||||
};
|
||||
},
|
||||
setCurrentConversation(state, { payload }) {
|
||||
const messageList = payload?.message.map((x: Message | IMessage) => ({
|
||||
...x,
|
||||
id: 'id' in x ? x.id : uuid(),
|
||||
}));
|
||||
const messageList =
|
||||
payload?.message?.map((x: Message | IMessage) => ({
|
||||
...x,
|
||||
id: 'id' in x ? x.id : uuid(),
|
||||
})) ?? [];
|
||||
return {
|
||||
...state,
|
||||
currentConversation: { ...payload, message: messageList },
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { variableEnabledFieldMap } from './constants';
|
||||
import { EmptyConversationId, variableEnabledFieldMap } from './constants';
|
||||
|
||||
export const excludeUnEnabledVariables = (values: any) => {
|
||||
const unEnabledFields: Array<keyof typeof variableEnabledFieldMap> =
|
||||
@ -10,3 +10,7 @@ export const excludeUnEnabledVariables = (values: any) => {
|
||||
(key) => `llm_setting.${variableEnabledFieldMap[key]}`,
|
||||
);
|
||||
};
|
||||
|
||||
export const isConversationIdNotExist = (conversationId: string) => {
|
||||
return conversationId !== EmptyConversationId && conversationId !== '';
|
||||
};
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { ITenantInfo } from '@/interfaces/database/knowledge';
|
||||
import { IThirdOAIModelCollection as IThirdAiModelCollection } from '@/interfaces/database/llm';
|
||||
import { IUserInfo } from '@/interfaces/database/userSetting';
|
||||
import userService from '@/services/userService';
|
||||
import authorizationUtil from '@/utils/authorizationUtil';
|
||||
import { message } from 'antd';
|
||||
import { Nullable } from 'typings';
|
||||
import { DvaModel } from 'umi';
|
||||
@ -16,6 +16,7 @@ export interface SettingModelState {
|
||||
llmInfo: IThirdAiModelCollection;
|
||||
myLlm: any[];
|
||||
factoriesList: any[];
|
||||
userInfo: IUserInfo;
|
||||
}
|
||||
|
||||
const model: DvaModel<SettingModelState> = {
|
||||
@ -30,6 +31,7 @@ const model: DvaModel<SettingModelState> = {
|
||||
llmInfo: {},
|
||||
myLlm: [],
|
||||
factoriesList: [],
|
||||
userInfo: {} as IUserInfo,
|
||||
},
|
||||
reducers: {
|
||||
updateState(state, { payload }) {
|
||||
@ -38,10 +40,11 @@ const model: DvaModel<SettingModelState> = {
|
||||
...payload,
|
||||
};
|
||||
},
|
||||
},
|
||||
subscriptions: {
|
||||
setup({ dispatch, history }) {
|
||||
history.listen((location) => {});
|
||||
setUserInfo(state, { payload }) {
|
||||
return {
|
||||
...state,
|
||||
userInfo: payload,
|
||||
};
|
||||
},
|
||||
},
|
||||
effects: {
|
||||
@ -63,15 +66,17 @@ const model: DvaModel<SettingModelState> = {
|
||||
}
|
||||
},
|
||||
*getUserInfo({ payload = {} }, { call, put }) {
|
||||
const { data, response } = yield call(userService.user_info, payload);
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
const userInfo = {
|
||||
avatar: res.avatar,
|
||||
name: res.nickname,
|
||||
email: res.email,
|
||||
};
|
||||
authorizationUtil.setUserInfo(userInfo);
|
||||
const { data } = yield call(userService.user_info, payload);
|
||||
const { retcode, data: res } = data;
|
||||
|
||||
// const userInfo = {
|
||||
// avatar: res.avatar,
|
||||
// name: res.nickname,
|
||||
// email: res.email,
|
||||
// };
|
||||
// authorizationUtil.setUserInfo(userInfo);
|
||||
if (retcode === 0) {
|
||||
yield put({ type: 'setUserInfo', payload: res });
|
||||
// localStorage.setItem('userInfo',res.)
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user