Browse Source

feat: rename conversation and delete conversation and preview reference image and fetch file thumbnails (#79)

* feat: fetch file thumbnails

* feat: preview reference image

* feat: delete conversation

* feat: rename conversation
tags/v0.1.0
balibabu 1 year ago
parent
commit
01ab5b5db1
No account linked to committer's email address

+ 78
- 0
web/src/components/rename-modal/index.tsx View File

@@ -0,0 +1,78 @@
import { Form, Input, Modal } from 'antd';
import { useEffect } from 'react';
import { IModalManagerChildrenProps } from '../modal-manager';

interface IProps extends Omit<IModalManagerChildrenProps, 'showModal'> {
loading: boolean;
initialName: string;
onOk: (name: string) => void;
showModal?(): void;
}

const RenameModal = ({
visible,
hideModal,
loading,
initialName,
onOk,
}: IProps) => {
const [form] = Form.useForm();

type FieldType = {
name?: string;
};

const handleOk = async () => {
const ret = await form.validateFields();

return onOk(ret.name);
};

const handleCancel = () => {
hideModal();
};

const onFinish = (values: any) => {
console.log('Success:', values);
};

const onFinishFailed = (errorInfo: any) => {
console.log('Failed:', errorInfo);
};

useEffect(() => {
form.setFieldValue('name', initialName);
}, [initialName, form]);

return (
<Modal
title="Rename"
open={visible}
onOk={handleOk}
onCancel={handleCancel}
okButtonProps={{ loading }}
confirmLoading={loading}
>
<Form
name="basic"
labelCol={{ span: 4 }}
wrapperCol={{ span: 20 }}
style={{ maxWidth: 600 }}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
autoComplete="off"
form={form}
>
<Form.Item<FieldType>
label="Name"
name="name"
rules={[{ required: true, message: 'Please input name!' }]}
>
<Input />
</Form.Item>
</Form>
</Modal>
);
};

export default RenameModal;

+ 31
- 0
web/src/hooks/knowledgeHook.ts View File

@@ -150,3 +150,34 @@ export const useFetchKnowledgeList = (

return list;
};

export const useSelectFileThumbnails = () => {
const fileThumbnails: Record<string, string> = useSelector(
(state: any) => state.kFModel.fileThumbnails,
);

return fileThumbnails;
};

export const useFetchFileThumbnails = (docIds?: Array<string>) => {
const dispatch = useDispatch();
const fileThumbnails = useSelectFileThumbnails();

const fetchFileThumbnails = useCallback(
(docIds: Array<string>) => {
dispatch({
type: 'kFModel/fetch_document_thumbnails',
payload: { doc_ids: docIds.join(',') },
});
},
[dispatch],
);

useEffect(() => {
if (docIds) {
fetchFileThumbnails(docIds);
}
}, [docIds, fetchFileThumbnails]);

return { fileThumbnails, fetchFileThumbnails };
};

+ 5
- 3
web/src/pages/add-knowledge/components/knowledge-file/index.less View File

@@ -20,9 +20,11 @@
}
.img {
height: 16px;
width: 16px;
margin-right: 6px;
height: 24px;
width: 24px;
margin-right: 10px;
display: inline-block;
vertical-align: middle;
}
.column {

+ 32
- 30
web/src/pages/add-knowledge/components/knowledge-file/index.tsx View File

@@ -22,7 +22,7 @@ import {
} from 'antd';
import type { ColumnsType } from 'antd/es/table';
import { PaginationProps } from 'antd/lib';
import React, { useEffect, useMemo, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Link, useDispatch, useNavigate, useSelector } from 'umi';
import CreateEPModal from './createEFileModal';
import styles from './index.less';
@@ -46,7 +46,7 @@ const KnowledgeFile = () => {
const [parser_id, setParserId] = useState('0');
let navigate = useNavigate();
const getKfList = () => {
const getKfList = useCallback(() => {
const payload = {
kb_id: knowledgeBaseId,
};
@@ -55,7 +55,7 @@ const KnowledgeFile = () => {
type: 'kFModel/getKfList',
payload,
});
};
}, [dispatch, knowledgeBaseId]);
const throttledGetDocumentList = () => {
dispatch({
@@ -64,23 +64,29 @@ const KnowledgeFile = () => {
});
};
const setPagination = (pageNumber = 1, pageSize?: number) => {
const pagination: Pagination = {
current: pageNumber,
} as Pagination;
if (pageSize) {
pagination.pageSize = pageSize;
}
dispatch({
type: 'kFModel/setPagination',
payload: pagination,
});
};
const setPagination = useCallback(
(pageNumber = 1, pageSize?: number) => {
const pagination: Pagination = {
current: pageNumber,
} as Pagination;
if (pageSize) {
pagination.pageSize = pageSize;
}
dispatch({
type: 'kFModel/setPagination',
payload: pagination,
});
},
[dispatch],
);
const onPageChange: PaginationProps['onChange'] = (pageNumber, pageSize) => {
setPagination(pageNumber, pageSize);
getKfList();
};
const onPageChange: PaginationProps['onChange'] = useCallback(
(pageNumber: number, pageSize: number) => {
setPagination(pageNumber, pageSize);
getKfList();
},
[getKfList, setPagination],
);
const pagination: PaginationProps = useMemo(() => {
return {
@@ -92,7 +98,7 @@ const KnowledgeFile = () => {
pageSizeOptions: [1, 2, 10, 20, 50, 100],
onChange: onPageChange,
};
}, [total, kFModel.pagination]);
}, [total, kFModel.pagination, onPageChange]);
useEffect(() => {
if (knowledgeBaseId) {
@@ -107,7 +113,7 @@ const KnowledgeFile = () => {
type: 'kFModel/pollGetDocumentList-stop',
});
};
}, [knowledgeBaseId]);
}, [knowledgeBaseId, dispatch, getKfList]);
const handleInputChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
@@ -129,14 +135,14 @@ const KnowledgeFile = () => {
});
};
const showCEFModal = () => {
const showCEFModal = useCallback(() => {
dispatch({
type: 'kFModel/updateState',
payload: {
isShowCEFwModal: true,
},
});
};
}, [dispatch]);
const actionItems: MenuProps['items'] = useMemo(() => {
return [
@@ -169,7 +175,7 @@ const KnowledgeFile = () => {
// disabled: true,
},
];
}, []);
}, [knowledgeBaseId, showCEFModal]);
const toChunk = (id: string) => {
navigate(
@@ -187,13 +193,9 @@ const KnowledgeFile = () => {
title: 'Name',
dataIndex: 'name',
key: 'name',
render: (text: any, { id }) => (
render: (text: any, { id, thumbnail }) => (
<div className={styles.tochunks} onClick={() => toChunk(id)}>
<img
className={styles.img}
src="https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg"
alt=""
/>
<img className={styles.img} src={thumbnail} alt="" />
{text}
</div>
),

+ 11
- 0
web/src/pages/add-knowledge/components/knowledge-file/model.ts View File

@@ -16,6 +16,7 @@ export interface KFModelState extends BaseState {
data: IKnowledgeFile[];
total: number;
currentRecord: Nullable<IKnowledgeFile>;
fileThumbnails: Record<string, string>;
}
const model: DvaModel<KFModelState> = {
@@ -34,6 +35,7 @@ const model: DvaModel<KFModelState> = {
current: 1,
pageSize: 10,
},
fileThumbnails: {} as Record<string, string>,
},
reducers: {
updateState(state, { payload }) {
@@ -54,6 +56,9 @@ const model: DvaModel<KFModelState> = {
setPagination(state, { payload }) {
return { ...state, pagination: { ...state.pagination, ...payload } };
},
setFileThumbnails(state, { payload }) {
return { ...state, fileThumbnails: payload };
},
},
effects: {
*createKf({ payload = {} }, { call }) {
@@ -201,6 +206,12 @@ const model: DvaModel<KFModelState> = {
}
return retcode;
},
*fetch_document_thumbnails({ payload = {} }, { call, put }) {
const { data } = yield call(kbService.document_thumbnails, payload);
if (data.retcode === 0) {
yield put({ type: 'setFileThumbnails', payload: data.data });
}
},
},
};
export default model;

+ 4
- 0
web/src/pages/chat/chat-container/index.less View File

@@ -47,3 +47,7 @@
.referenceChunkImage {
width: 10vw;
}

.referenceImagePreview {
width: 600px;
}

+ 33
- 29
web/src/pages/chat/chat-container/index.tsx View File

@@ -3,21 +3,12 @@ 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,
List,
Popover,
Space,
Typography,
} from 'antd';
import { Avatar, Button, Flex, Input, List, Popover, Space } from 'antd';
import classNames from 'classnames';
import { ChangeEventHandler, useCallback, useMemo, useState } from 'react';
import reactStringReplace from 'react-string-replace';
import {
useFetchConversation,
useFetchConversationOnMount,
useGetFileIcon,
useScrollToBottom,
useSendMessage,
@@ -26,6 +17,7 @@ import { IClientConversation } from '../interface';

import Image from '@/components/image';
import NewDocumentLink from '@/components/new-document-link';
import { useSelectFileThumbnails } from '@/hooks/knowledgeHook';
import { InfoCircleOutlined } from '@ant-design/icons';
import Markdown from 'react-markdown';
import { visitParents } from 'unist-util-visit-parents';
@@ -56,11 +48,10 @@ const MessageItem = ({
reference: IReference;
}) => {
const userInfo = useSelectUserInfo();
const fileThumbnails = useSelectFileThumbnails();

const isAssistant = item.role === MessageType.Assistant;

const getFileIcon = useGetFileIcon();

const getPopoverContent = useCallback(
(chunkIndex: number) => {
const chunks = reference?.chunks ?? [];
@@ -75,22 +66,35 @@ const MessageItem = ({
gap={10}
className={styles.referencePopoverWrapper}
>
<Image
id={chunkItem?.img_id}
className={styles.referenceChunkImage}
></Image>
<Popover
placement="topRight"
content={
<Image
id={chunkItem?.img_id}
className={styles.referenceImagePreview}
></Image>
}
>
<Image
id={chunkItem?.img_id}
className={styles.referenceChunkImage}
></Image>
</Popover>
<Space direction={'vertical'}>
<div>{chunkItem?.content_with_weight}</div>
{documentId && (
<NewDocumentLink documentId={documentId}>
{document?.doc_name}
</NewDocumentLink>
<Flex gap={'middle'}>
<img src={fileThumbnails[documentId]} alt="" />
<NewDocumentLink documentId={documentId}>
{document?.doc_name}
</NewDocumentLink>
</Flex>
)}
</Space>
</Flex>
);
},
[reference],
[reference, fileThumbnails],
);

const renderReference = useCallback(
@@ -163,12 +167,13 @@ const MessageItem = ({
dataSource={referenceDocumentList}
renderItem={(item) => (
<List.Item>
<Typography.Text mark>
{/* <SvgIcon name={getFileIcon(item.doc_name)}></SvgIcon> */}
</Typography.Text>
<NewDocumentLink documentId={item.doc_id}>
{item.doc_name}
</NewDocumentLink>
{/* <SvgIcon name={getFileIcon(item.doc_name)}></SvgIcon> */}
<Flex gap={'middle'}>
<img src={fileThumbnails[item.doc_id]}></img>
<NewDocumentLink documentId={item.doc_id}>
{item.doc_name}
</NewDocumentLink>
</Flex>
</List.Item>
)}
/>
@@ -182,11 +187,10 @@ const MessageItem = ({

const ChatContainer = () => {
const [value, setValue] = useState('');
const conversation: IClientConversation = useFetchConversation();
const conversation: IClientConversation = useFetchConversationOnMount();
const { sendMessage } = useSendMessage();
const loading = useOneNamespaceEffectsLoading('chatModel', [
'completeConversation',
'getConversation',
]);
const ref = useScrollToBottom();
useGetFileIcon();

+ 131
- 12
web/src/pages/chat/hooks.ts View File

@@ -1,6 +1,8 @@
import showDeleteConfirm from '@/components/deleting-confirm';
import { MessageType } from '@/constants/chat';
import { fileIconMap } from '@/constants/common';
import { useSetModalState } from '@/hooks/commonHooks';
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
import { IConversation, IDialog } from '@/interfaces/database/chat';
import { getFileExtension } from '@/utils';
import omit from 'lodash/omit';
@@ -14,7 +16,7 @@ import {
VariableTableDataType,
} from './interface';
import { ChatModelState } from './model';
import { isConversationIdNotExist } from './utils';
import { isConversationIdExist } from './utils';

export const useFetchDialogList = () => {
const dispatch = useDispatch();
@@ -204,6 +206,24 @@ export const useSelectFirstDialogOnMount = () => {
return dialogList;
};

export const useHandleItemHover = () => {
const [activated, setActivated] = useState<string>('');

const handleItemEnter = (id: string) => {
setActivated(id);
};

const handleItemLeave = () => {
setActivated('');
};

return {
activated,
handleItemEnter,
handleItemLeave,
};
};

//#region conversation

export const useCreateTemporaryConversation = () => {
@@ -374,30 +394,50 @@ export const useSetConversation = () => {
return { setConversation };
};

export const useFetchConversation = () => {
const dispatch = useDispatch();
const { conversationId } = useGetChatSearchParams();
const conversation = useSelector(
export const useSelectCurrentConversation = () => {
const conversation: IClientConversation = useSelector(
(state: any) => state.chatModel.currentConversation,
);
const setCurrentConversation = useSetCurrentConversation();

const fetchConversation = useCallback(() => {
if (isConversationIdNotExist(conversationId)) {
dispatch<any>({
return conversation;
};

export const useFetchConversation = () => {
const dispatch = useDispatch();

const fetchConversation = useCallback(
(conversationId: string, needToBeSaved = true) => {
return dispatch<any>({
type: 'chatModel/getConversation',
payload: {
needToBeSaved,
conversation_id: conversationId,
},
});
},
[dispatch],
);

return fetchConversation;
};

export const useFetchConversationOnMount = () => {
const { conversationId } = useGetChatSearchParams();
const conversation = useSelectCurrentConversation();
const setCurrentConversation = useSetCurrentConversation();
const fetchConversation = useFetchConversation();

const fetchConversationOnMount = useCallback(() => {
if (isConversationIdExist(conversationId)) {
fetchConversation(conversationId);
} else {
setCurrentConversation({} as IClientConversation);
}
}, [dispatch, conversationId, setCurrentConversation]);
}, [fetchConversation, setCurrentConversation, conversationId]);

useEffect(() => {
fetchConversation();
}, [fetchConversation]);
fetchConversationOnMount();
}, [fetchConversationOnMount]);

return conversation;
};
@@ -477,4 +517,83 @@ export const useGetFileIcon = () => {
return getFileIcon;
};

export const useRemoveConversation = () => {
const dispatch = useDispatch();
const { dialogId } = useGetChatSearchParams();
const { handleClickConversation } = useClickConversationCard();

const removeConversation = (conversationIds: Array<string>) => async () => {
const ret = await dispatch<any>({
type: 'chatModel/removeConversation',
payload: {
dialog_id: dialogId,
conversation_ids: conversationIds,
},
});

if (ret === 0) {
handleClickConversation('');
}

return ret;
};

const onRemoveConversation = (conversationIds: Array<string>) => {
showDeleteConfirm({ onOk: removeConversation(conversationIds) });
};

return { onRemoveConversation };
};

export const useRenameConversation = () => {
const dispatch = useDispatch();
const [conversation, setConversation] = useState<IClientConversation>(
{} as IClientConversation,
);
const fetchConversation = useFetchConversation();
const {
visible: conversationRenameVisible,
hideModal: hideConversationRenameModal,
showModal: showConversationRenameModal,
} = useSetModalState();

const onConversationRenameOk = useCallback(
async (name: string) => {
const ret = await dispatch<any>({
type: 'chatModel/setConversation',
payload: { ...conversation, conversation_id: conversation.id, name },
});

if (ret.retcode === 0) {
hideConversationRenameModal();
}
},
[dispatch, conversation, hideConversationRenameModal],
);

const loading = useOneNamespaceEffectsLoading('chatModel', [
'setConversation',
]);

const handleShowConversationRenameModal = useCallback(
async (conversationId: string) => {
const ret = await fetchConversation(conversationId, false);
if (ret.retcode === 0) {
setConversation(ret.data);
}
showConversationRenameModal();
},
[showConversationRenameModal, fetchConversation],
);

return {
conversationRenameLoading: loading,
initialConversationName: conversation.name,
onConversationRenameOk,
conversationRenameVisible,
hideConversationRenameModal,
showConversationRenameModal: handleShowConversationRenameModal,
};
};

//#endregion

+ 107
- 14
web/src/pages/chat/index.tsx View File

@@ -11,8 +11,9 @@ import {
Space,
Tag,
} from 'antd';
import { MenuItemProps } from 'antd/lib/menu/MenuItem';
import classNames from 'classnames';
import { useCallback, useState } from 'react';
import { useCallback } from 'react';
import ChatConfigurationModal from './chat-configuration-modal';
import ChatContainer from './chat-container';
import {
@@ -21,42 +22,88 @@ import {
useFetchConversationList,
useFetchDialog,
useGetChatSearchParams,
useHandleItemHover,
useRemoveConversation,
useRemoveDialog,
useRenameConversation,
useSelectConversationList,
useSelectFirstDialogOnMount,
useSetCurrentDialog,
} from './hooks';
import RenameModal from '@/components/rename-modal';
import styles from './index.less';
const Chat = () => {
const dialogList = useSelectFirstDialogOnMount();
const [activated, setActivated] = useState<string>('');
const { visible, hideModal, showModal } = useSetModalState();
const { setCurrentDialog, currentDialog } = useSetCurrentDialog();
const { onRemoveDialog } = useRemoveDialog();
const { onRemoveConversation } = useRemoveConversation();
const { handleClickDialog } = useClickDialogCard();
const { handleClickConversation } = useClickConversationCard();
const { dialogId, conversationId } = useGetChatSearchParams();
const { list: conversationList, addTemporaryConversation } =
useSelectConversationList();
const { activated, handleItemEnter, handleItemLeave } = useHandleItemHover();
const {
activated: conversationActivated,
handleItemEnter: handleConversationItemEnter,
handleItemLeave: handleConversationItemLeave,
} = useHandleItemHover();
const {
conversationRenameLoading,
initialConversationName,
onConversationRenameOk,
conversationRenameVisible,
hideConversationRenameModal,
showConversationRenameModal,
} = useRenameConversation();
useFetchDialog(dialogId, true);
const handleAppCardEnter = (id: string) => () => {
setActivated(id);
handleItemEnter(id);
};
const handleAppCardLeave = () => {
setActivated('');
const handleConversationCardEnter = (id: string) => () => {
handleConversationItemEnter(id);
};
const handleShowChatConfigurationModal = (dialogId?: string) => () => {
if (dialogId) {
setCurrentDialog(dialogId);
}
showModal();
};
const handleShowChatConfigurationModal =
(dialogId?: string): any =>
(info: any) => {
info?.domEvent?.preventDefault();
info?.domEvent?.stopPropagation();
if (dialogId) {
setCurrentDialog(dialogId);
}
showModal();
};
const handleRemoveDialog =
(dialogId: string): MenuItemProps['onClick'] =>
({ domEvent }) => {
domEvent.preventDefault();
domEvent.stopPropagation();
onRemoveDialog([dialogId]);
};
const handleRemoveConversation =
(conversationId: string): MenuItemProps['onClick'] =>
({ domEvent }) => {
domEvent.preventDefault();
domEvent.stopPropagation();
onRemoveConversation([conversationId]);
};
const handleShowConversationRenameModal =
(conversationId: string): MenuItemProps['onClick'] =>
({ domEvent }) => {
domEvent.preventDefault();
domEvent.stopPropagation();
showConversationRenameModal(conversationId);
};
const handleDialogCardClick = (dialogId: string) => () => {
handleClickDialog(dialogId);
@@ -97,7 +144,35 @@ const Chat = () => {
{ type: 'divider' },
{
key: '2',
onClick: () => onRemoveDialog([dialogId]),
onClick: handleRemoveDialog(dialogId),
label: (
<Space>
<DeleteOutlined />
Delete chat
</Space>
),
},
];
return appItems;
};
const buildConversationItems = (conversationId: string) => {
const appItems: MenuProps['items'] = [
{
key: '1',
onClick: handleShowConversationRenameModal(conversationId),
label: (
<Space>
<EditOutlined />
Edit
</Space>
),
},
{ type: 'divider' },
{
key: '2',
onClick: handleRemoveConversation(conversationId),
label: (
<Space>
<DeleteOutlined />
@@ -129,7 +204,7 @@ const Chat = () => {
[styles.chatAppCardSelected]: dialogId === x.id,
})}
onMouseEnter={handleAppCardEnter(x.id)}
onMouseLeave={handleAppCardLeave}
onMouseLeave={handleItemLeave}
onClick={handleDialogCardClick(x.id)}
>
<Flex justify="space-between" align="center">
@@ -176,11 +251,22 @@ const Chat = () => {
key={x.id}
hoverable
onClick={handleConversationCardClick(x.id)}
onMouseEnter={handleConversationCardEnter(x.id)}
onMouseLeave={handleConversationItemLeave}
className={classNames(styles.chatTitleCard, {
[styles.chatTitleCardSelected]: x.id === conversationId,
})}
>
<div>{x.name}</div>
<Flex justify="space-between" align="center">
<div>{x.name}</div>
{conversationActivated === x.id && x.id !== '' && (
<section>
<Dropdown menu={{ items: buildConversationItems(x.id) }}>
<ChatAppCube className={styles.cubeIcon}></ChatAppCube>
</Dropdown>
</section>
)}
</Flex>
</Card>
))}
</Flex>
@@ -194,6 +280,13 @@ const Chat = () => {
hideModal={hideModal}
id={currentDialog.id}
></ChatConfigurationModal>
<RenameModal
visible={conversationRenameVisible}
hideModal={hideConversationRenameModal}
onOk={onConversationRenameOk}
initialName={initialConversationName}
loading={conversationRenameLoading}
></RenameModal>
</Flex>
);
};

+ 25
- 3
web/src/pages/chat/model.ts View File

@@ -4,6 +4,7 @@ import { message } from 'antd';
import { DvaModel } from 'umi';
import { v4 as uuid } from 'uuid';
import { IClientConversation, IMessage } from './interface';
import { getDocumentIdsFromConversionReference } from './utils';
export interface ChatModelState {
name: string;
@@ -109,11 +110,19 @@ const model: DvaModel<ChatModelState> = {
return data.retcode;
},
*getConversation({ payload }, { call, put }) {
const { data } = yield call(chatService.getConversation, payload);
if (data.retcode === 0) {
const { data } = yield call(chatService.getConversation, {
conversation_id: payload.conversation_id,
});
if (data.retcode === 0 && payload.needToBeSaved) {
yield put({
type: 'kFModel/fetch_document_thumbnails',
payload: {
doc_ids: getDocumentIdsFromConversionReference(data.data),
},
});
yield put({ type: 'setCurrentConversation', payload: data.data });
}
return data.retcode;
return data;
},
*setConversation({ payload }, { call, put }) {
const { data } = yield call(chatService.setConversation, payload);
@@ -138,6 +147,19 @@ const model: DvaModel<ChatModelState> = {
});
}
},
*removeConversation({ payload }, { call, put }) {
const { data } = yield call(chatService.removeConversation, {
conversation_ids: payload.conversation_ids,
});
if (data.retcode === 0) {
yield put({
type: 'listConversation',
payload: { dialog_id: payload.dialog_id },
});
message.success('Deleted successfully !');
}
return data.retcode;
},
},
};

+ 19
- 1
web/src/pages/chat/utils.ts View File

@@ -1,3 +1,4 @@
import { IConversation, IReference } from '@/interfaces/database/chat';
import { EmptyConversationId, variableEnabledFieldMap } from './constants';

export const excludeUnEnabledVariables = (values: any) => {
@@ -11,6 +12,23 @@ export const excludeUnEnabledVariables = (values: any) => {
);
};

export const isConversationIdNotExist = (conversationId: string) => {
export const isConversationIdExist = (conversationId: string) => {
return conversationId !== EmptyConversationId && conversationId !== '';
};

export const getDocumentIdsFromConversionReference = (data: IConversation) => {
const documentIds = data.reference.reduce(
(pre: Array<string>, cur: IReference) => {
cur.doc_aggs
.map((x) => x.doc_id)
.forEach((x) => {
if (pre.every((y) => y !== x)) {
pre.push(x);
}
});
return pre;
},
[],
);
return documentIds.join(',');
};

+ 5
- 0
web/src/services/chatService.ts View File

@@ -11,6 +11,7 @@ const {
setConversation,
completeConversation,
listConversation,
removeConversation,
} = api;

const methods = {
@@ -46,6 +47,10 @@ const methods = {
url: completeConversation,
method: 'post',
},
removeConversation: {
url: removeConversation,
method: 'post',
},
} as const;

const chatService = registerServer<keyof typeof methods>(methods, request);

+ 5
- 0
web/src/services/kbService.ts View File

@@ -13,6 +13,7 @@ const {
document_rm,
document_create,
document_change_parser,
document_thumbnails,
chunk_list,
create_chunk,
set_chunk,
@@ -75,6 +76,10 @@ const methods = {
url: document_change_parser,
method: 'post',
},
document_thumbnails: {
url: document_thumbnails,
method: 'get',
},
// chunk管理
chunk_list: {
url: chunk_list,

+ 1
- 0
web/src/utils/api.ts View File

@@ -42,6 +42,7 @@ export default {
document_create: `${api_host}/document/create`,
document_run: `${api_host}/document/run`,
document_change_parser: `${api_host}/document/change_parser`,
document_thumbnails: `${api_host}/document/thumbnails`,
setDialog: `${api_host}/dialog/set`,
getDialog: `${api_host}/dialog/get`,

Loading…
Cancel
Save