mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
feat: fetch knowledge detail on KnowledgeUploadFile mount and add category column to chunk table and set initial value for the model field of chat setting (#104)
* feat: set initial value for the model field of chat setting * feat: add category column to chunk table * feat: fetch knowledge detail on KnowledgeUploadFile mount
This commit is contained in:
@ -1,6 +1,6 @@
|
|||||||
import showDeleteConfirm from '@/components/deleting-confirm';
|
import showDeleteConfirm from '@/components/deleting-confirm';
|
||||||
import { KnowledgeSearchParams } from '@/constants/knowledge';
|
import { KnowledgeSearchParams } from '@/constants/knowledge';
|
||||||
import { IKnowledge, ITenantInfo } from '@/interfaces/database/knowledge';
|
import { IKnowledge } from '@/interfaces/database/knowledge';
|
||||||
import { useCallback, useEffect, useMemo } from 'react';
|
import { useCallback, useEffect, useMemo } from 'react';
|
||||||
import { useDispatch, useSearchParams, useSelector } from 'umi';
|
import { useDispatch, useSearchParams, useSelector } from 'umi';
|
||||||
|
|
||||||
@ -11,6 +11,17 @@ export const useKnowledgeBaseId = (): string => {
|
|||||||
return knowledgeBaseId || '';
|
return knowledgeBaseId || '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useGetKnowledgeSearchParams = () => {
|
||||||
|
const [currentQueryParameters] = useSearchParams();
|
||||||
|
|
||||||
|
return {
|
||||||
|
documentId:
|
||||||
|
currentQueryParameters.get(KnowledgeSearchParams.DocumentId) || '',
|
||||||
|
knowledgeId:
|
||||||
|
currentQueryParameters.get(KnowledgeSearchParams.KnowledgeId) || '',
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const useDeleteDocumentById = (): {
|
export const useDeleteDocumentById = (): {
|
||||||
removeDocument: (documentId: string) => Promise<number>;
|
removeDocument: (documentId: string) => Promise<number>;
|
||||||
} => {
|
} => {
|
||||||
@ -36,12 +47,37 @@ export const useDeleteDocumentById = (): {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGetDocumentDefaultParser = (knowledgeBaseId: string) => {
|
export const useFetchKnowledgeDetail = () => {
|
||||||
const data: IKnowledge[] = useSelector(
|
const dispatch = useDispatch();
|
||||||
(state: any) => state.knowledgeModel.data,
|
const { knowledgeId } = useGetKnowledgeSearchParams();
|
||||||
|
|
||||||
|
const fetchKnowledgeDetail = useCallback(
|
||||||
|
(knowledgeId: string) => {
|
||||||
|
dispatch({
|
||||||
|
type: 'knowledgeModel/getKnowledgeDetail',
|
||||||
|
payload: { kb_id: knowledgeId },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[dispatch],
|
||||||
);
|
);
|
||||||
|
|
||||||
const item = data.find((x) => x.id === knowledgeBaseId);
|
useEffect(() => {
|
||||||
|
fetchKnowledgeDetail(knowledgeId);
|
||||||
|
}, [fetchKnowledgeDetail, knowledgeId]);
|
||||||
|
|
||||||
|
return fetchKnowledgeDetail;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSelectKnowledgeDetail = () => {
|
||||||
|
const knowledge: IKnowledge = useSelector(
|
||||||
|
(state: any) => state.knowledgeModel.knowledge,
|
||||||
|
);
|
||||||
|
|
||||||
|
return knowledge;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGetDocumentDefaultParser = () => {
|
||||||
|
const item = useSelectKnowledgeDetail();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
defaultParserId: item?.parser_id ?? '',
|
defaultParserId: item?.parser_id ?? '',
|
||||||
@ -79,35 +115,6 @@ export const useDeleteChunkByIds = (): {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useSelectParserList = (): Array<{
|
|
||||||
value: string;
|
|
||||||
label: string;
|
|
||||||
}> => {
|
|
||||||
const tenantIfo: Nullable<ITenantInfo> = useSelector(
|
|
||||||
(state: any) => state.settingModel.tenantIfo,
|
|
||||||
);
|
|
||||||
|
|
||||||
const parserList = useMemo(() => {
|
|
||||||
const parserArray: Array<string> = tenantIfo?.parser_ids.split(',') ?? [];
|
|
||||||
return parserArray.map((x) => {
|
|
||||||
const arr = x.split(':');
|
|
||||||
return { value: arr[0], label: arr[1] };
|
|
||||||
});
|
|
||||||
}, [tenantIfo]);
|
|
||||||
|
|
||||||
return parserList;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useFetchParserList = () => {
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
dispatch({
|
|
||||||
type: 'settingModel/getTenantInfo',
|
|
||||||
});
|
|
||||||
}, [dispatch]);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useFetchKnowledgeBaseConfiguration = () => {
|
export const useFetchKnowledgeBaseConfiguration = () => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const knowledgeBaseId = useKnowledgeBaseId();
|
const knowledgeBaseId = useKnowledgeBaseId();
|
||||||
@ -182,14 +189,3 @@ export const useFetchFileThumbnails = (docIds?: Array<string>) => {
|
|||||||
|
|
||||||
return { fileThumbnails, fetchFileThumbnails };
|
return { fileThumbnails, fetchFileThumbnails };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGetKnowledgeSearchParams = () => {
|
|
||||||
const [currentQueryParameters] = useSearchParams();
|
|
||||||
|
|
||||||
return {
|
|
||||||
documentId:
|
|
||||||
currentQueryParameters.get(KnowledgeSearchParams.DocumentId) || '',
|
|
||||||
knowledgeId:
|
|
||||||
currentQueryParameters.get(KnowledgeSearchParams.KnowledgeId) || '',
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
|
import { ITenantInfo } from '@/interfaces/database/knowledge';
|
||||||
import { IUserInfo } from '@/interfaces/database/userSetting';
|
import { IUserInfo } from '@/interfaces/database/userSetting';
|
||||||
import { useCallback, useEffect } from 'react';
|
import { useCallback, useEffect, useMemo } from 'react';
|
||||||
import { useDispatch, useSelector } from 'umi';
|
import { useDispatch, useSelector } from 'umi';
|
||||||
|
|
||||||
export const useFetchUserInfo = () => {
|
export const useFetchUserInfo = () => {
|
||||||
@ -20,3 +21,46 @@ export const useSelectUserInfo = () => {
|
|||||||
|
|
||||||
return userInfo;
|
return userInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useSelectTenantInfo = () => {
|
||||||
|
const tenantInfo: ITenantInfo = useSelector(
|
||||||
|
(state: any) => state.settingModel.tenantIfo,
|
||||||
|
);
|
||||||
|
|
||||||
|
return tenantInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useFetchTenantInfo = (isOnMountFetching: boolean = true) => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const fetchTenantInfo = useCallback(() => {
|
||||||
|
dispatch({
|
||||||
|
type: 'settingModel/getTenantInfo',
|
||||||
|
});
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isOnMountFetching) {
|
||||||
|
fetchTenantInfo();
|
||||||
|
}
|
||||||
|
}, [fetchTenantInfo, isOnMountFetching]);
|
||||||
|
|
||||||
|
return fetchTenantInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSelectParserList = (): Array<{
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
}> => {
|
||||||
|
const tenantInfo: ITenantInfo = useSelectTenantInfo();
|
||||||
|
|
||||||
|
const parserList = useMemo(() => {
|
||||||
|
const parserArray: Array<string> = tenantInfo?.parser_ids.split(',') ?? [];
|
||||||
|
return parserArray.map((x) => {
|
||||||
|
const arr = x.split(':');
|
||||||
|
return { value: arr[0], label: arr[1] };
|
||||||
|
});
|
||||||
|
}, [tenantInfo]);
|
||||||
|
|
||||||
|
return parserList;
|
||||||
|
};
|
||||||
|
|||||||
@ -23,19 +23,13 @@ const App: React.FC = () => {
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
key: '1',
|
key: '1',
|
||||||
label: (
|
onClick: logout,
|
||||||
<Button type="text" onClick={logout}>
|
label: <Button type="text">{t('header.logout')}</Button>,
|
||||||
{t('header.logout')}
|
|
||||||
</Button>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: '2',
|
key: '2',
|
||||||
label: (
|
onClick: toSetting,
|
||||||
<Button type="text" onClick={toSetting}>
|
label: <Button type="text">{t('header.setting')}</Button>,
|
||||||
{t('header.setting')}
|
|
||||||
</Button>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}, [t]);
|
}, [t]);
|
||||||
|
|||||||
@ -2,11 +2,15 @@ import { ReactComponent as SelectFilesEndIcon } from '@/assets/svg/select-files-
|
|||||||
import { ReactComponent as SelectFilesStartIcon } from '@/assets/svg/select-files-start.svg';
|
import { ReactComponent as SelectFilesStartIcon } from '@/assets/svg/select-files-start.svg';
|
||||||
import {
|
import {
|
||||||
useDeleteDocumentById,
|
useDeleteDocumentById,
|
||||||
useFetchParserList,
|
useFetchKnowledgeDetail,
|
||||||
useGetDocumentDefaultParser,
|
useGetDocumentDefaultParser,
|
||||||
useKnowledgeBaseId,
|
useKnowledgeBaseId,
|
||||||
useSelectParserList,
|
|
||||||
} from '@/hooks/knowledgeHook';
|
} from '@/hooks/knowledgeHook';
|
||||||
|
import {
|
||||||
|
useFetchTenantInfo,
|
||||||
|
useSelectParserList,
|
||||||
|
} from '@/hooks/userSettingHook';
|
||||||
|
|
||||||
import uploadService from '@/services/uploadService';
|
import uploadService from '@/services/uploadService';
|
||||||
import {
|
import {
|
||||||
ArrowLeftOutlined,
|
ArrowLeftOutlined,
|
||||||
@ -29,10 +33,18 @@ import {
|
|||||||
UploadProps,
|
UploadProps,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { ReactElement, useEffect, useRef, useState } from 'react';
|
import {
|
||||||
|
ReactElement,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
import { Link, useDispatch, useNavigate } from 'umi';
|
import { Link, useDispatch, useNavigate } from 'umi';
|
||||||
|
|
||||||
import { KnowledgeRouteKey } from '@/constants/knowledge';
|
import { KnowledgeRouteKey } from '@/constants/knowledge';
|
||||||
|
import { isFileUploadDone } from '@/utils/documentUtils';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
const { Dragger } = Upload;
|
const { Dragger } = Upload;
|
||||||
@ -43,18 +55,16 @@ type UploadRequestOption = Parameters<
|
|||||||
|
|
||||||
const UploaderItem = ({
|
const UploaderItem = ({
|
||||||
file,
|
file,
|
||||||
actions,
|
|
||||||
isUpload,
|
isUpload,
|
||||||
|
remove,
|
||||||
}: {
|
}: {
|
||||||
isUpload: boolean;
|
isUpload: boolean;
|
||||||
originNode: ReactElement;
|
originNode: ReactElement;
|
||||||
file: UploadFile;
|
file: UploadFile;
|
||||||
fileList: object[];
|
fileList: object[];
|
||||||
actions: { download: Function; preview: Function; remove: any };
|
remove: (id: string) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { parserConfig, defaultParserId } = useGetDocumentDefaultParser(
|
const { parserConfig, defaultParserId } = useGetDocumentDefaultParser();
|
||||||
file?.response?.kb_id,
|
|
||||||
);
|
|
||||||
const { removeDocument } = useDeleteDocumentById();
|
const { removeDocument } = useDeleteDocumentById();
|
||||||
const [value, setValue] = useState(defaultParserId);
|
const [value, setValue] = useState(defaultParserId);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
@ -97,9 +107,13 @@ const UploaderItem = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleRemove = async () => {
|
const handleRemove = async () => {
|
||||||
|
if (file.status === 'error') {
|
||||||
|
remove(documentId);
|
||||||
|
} else {
|
||||||
const ret: any = await removeDocument(documentId);
|
const ret: any = await removeDocument(documentId);
|
||||||
if (ret === 0) {
|
if (ret === 0) {
|
||||||
actions?.remove();
|
remove(documentId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -147,40 +161,67 @@ const KnowledgeUploadFile = () => {
|
|||||||
const knowledgeBaseId = useKnowledgeBaseId();
|
const knowledgeBaseId = useKnowledgeBaseId();
|
||||||
const [isUpload, setIsUpload] = useState(true);
|
const [isUpload, setIsUpload] = useState(true);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
const [uploadedFileIds, setUploadedFileIds] = useState<string[]>([]);
|
||||||
const navigate = useNavigate();
|
|
||||||
const fileListRef = useRef<UploadFile[]>([]);
|
const fileListRef = useRef<UploadFile[]>([]);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const enabled = useMemo(() => {
|
||||||
|
if (isUpload) {
|
||||||
|
return (
|
||||||
|
uploadedFileIds.length > 0 &&
|
||||||
|
fileListRef.current.filter((x) => isFileUploadDone(x)).length ===
|
||||||
|
uploadedFileIds.length
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}, [uploadedFileIds, isUpload]);
|
||||||
|
|
||||||
const createRequest: (props: UploadRequestOption) => void = async function ({
|
const createRequest: (props: UploadRequestOption) => void = async function ({
|
||||||
file,
|
file,
|
||||||
onSuccess,
|
onSuccess,
|
||||||
onError,
|
onError,
|
||||||
onProgress,
|
// onProgress,
|
||||||
}) {
|
}) {
|
||||||
const { data } = await uploadService.uploadFile(file, knowledgeBaseId);
|
const ret = await uploadService.uploadFile(file, knowledgeBaseId);
|
||||||
if (data.retcode === 0) {
|
const data = ret?.data;
|
||||||
|
if (data?.retcode === 0) {
|
||||||
|
setUploadedFileIds((pre) => {
|
||||||
|
return pre.concat(data.data.id);
|
||||||
|
});
|
||||||
if (onSuccess) {
|
if (onSuccess) {
|
||||||
onSuccess(data.data);
|
onSuccess(data.data);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (onError) {
|
if (onError) {
|
||||||
onError(data.data);
|
onError(data?.data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const removeIdFromUploadedIds = useCallback((id: string) => {
|
||||||
|
setUploadedFileIds((pre) => {
|
||||||
|
return pre.filter((x) => x !== id);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
const props: UploadProps = {
|
const props: UploadProps = {
|
||||||
name: 'file',
|
name: 'file',
|
||||||
multiple: true,
|
multiple: true,
|
||||||
itemRender(originNode, file, fileList, actions) {
|
itemRender(originNode, file, fileList, actions) {
|
||||||
fileListRef.current = fileList;
|
fileListRef.current = fileList;
|
||||||
|
const remove = (id: string) => {
|
||||||
|
if (isFileUploadDone(file)) {
|
||||||
|
removeIdFromUploadedIds(id);
|
||||||
|
}
|
||||||
|
actions.remove();
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<UploaderItem
|
<UploaderItem
|
||||||
isUpload={isUpload}
|
isUpload={isUpload}
|
||||||
file={file}
|
file={file}
|
||||||
fileList={fileList}
|
fileList={fileList}
|
||||||
originNode={originNode}
|
originNode={originNode}
|
||||||
actions={actions}
|
remove={remove}
|
||||||
></UploaderItem>
|
></UploaderItem>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -207,7 +248,8 @@ const KnowledgeUploadFile = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useFetchParserList();
|
useFetchTenantInfo();
|
||||||
|
useFetchKnowledgeDetail();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.uploadWrapper}>
|
<div className={styles.uploadWrapper}>
|
||||||
@ -263,8 +305,9 @@ const KnowledgeUploadFile = () => {
|
|||||||
<section className={styles.footer}>
|
<section className={styles.footer}>
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
className={styles.nextButton}
|
// className={styles.nextButton}
|
||||||
onClick={handleNextClick}
|
onClick={handleNextClick}
|
||||||
|
disabled={!enabled}
|
||||||
>
|
>
|
||||||
Next
|
Next
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
import { KnowledgeRouteKey } from '@/constants/knowledge';
|
import { KnowledgeRouteKey } from '@/constants/knowledge';
|
||||||
import { useKnowledgeBaseId } from '@/hooks/knowledgeHook';
|
import { useKnowledgeBaseId } from '@/hooks/knowledgeHook';
|
||||||
|
import {
|
||||||
|
useFetchTenantInfo,
|
||||||
|
useSelectParserList,
|
||||||
|
} from '@/hooks/userSettingHook';
|
||||||
import { Pagination } from '@/interfaces/common';
|
import { Pagination } from '@/interfaces/common';
|
||||||
import { IKnowledgeFile } from '@/interfaces/database/knowledge';
|
import { IKnowledgeFile } from '@/interfaces/database/knowledge';
|
||||||
import { getOneNamespaceEffectsLoading } from '@/utils/storeUtil';
|
import { getOneNamespaceEffectsLoading } from '@/utils/storeUtil';
|
||||||
@ -45,6 +49,7 @@ const KnowledgeFile = () => {
|
|||||||
const [doc_id, setDocId] = useState('0');
|
const [doc_id, setDocId] = useState('0');
|
||||||
const [parser_id, setParserId] = useState('0');
|
const [parser_id, setParserId] = useState('0');
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
|
const parserList = useSelectParserList();
|
||||||
|
|
||||||
const getKfList = useCallback(() => {
|
const getKfList = useCallback(() => {
|
||||||
const payload = {
|
const payload = {
|
||||||
@ -214,6 +219,14 @@ const KnowledgeFile = () => {
|
|||||||
dataIndex: 'create_date',
|
dataIndex: 'create_date',
|
||||||
key: 'create_date',
|
key: 'create_date',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Category',
|
||||||
|
dataIndex: 'parser_id',
|
||||||
|
key: 'parser_id',
|
||||||
|
render: (text) => {
|
||||||
|
return parserList.find((x) => x.value === text)?.label;
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'Parsing Status',
|
title: 'Parsing Status',
|
||||||
dataIndex: 'run',
|
dataIndex: 'run',
|
||||||
@ -255,6 +268,8 @@ const KnowledgeFile = () => {
|
|||||||
className: `${styles.column}`,
|
className: `${styles.column}`,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
useFetchTenantInfo();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.datasetWrapper}>
|
<div className={styles.datasetWrapper}>
|
||||||
<h3>Dataset</h3>
|
<h3>Dataset</h3>
|
||||||
|
|||||||
@ -62,7 +62,7 @@ const ParsingActionCell = ({
|
|||||||
label: (
|
label: (
|
||||||
<div>
|
<div>
|
||||||
<Button type="link" onClick={showSegmentSetModal}>
|
<Button type="link" onClick={showSegmentSetModal}>
|
||||||
Parser type
|
Category
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
|
|||||||
@ -1,4 +1,7 @@
|
|||||||
import { useFetchParserList, useSelectParserList } from '@/hooks/knowledgeHook';
|
import {
|
||||||
|
useFetchTenantInfo,
|
||||||
|
useSelectParserList,
|
||||||
|
} from '@/hooks/userSettingHook';
|
||||||
import { Modal, Space, Tag } from 'antd';
|
import { Modal, Space, Tag } from 'antd';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useDispatch, useSelector } from 'umi';
|
import { useDispatch, useSelector } from 'umi';
|
||||||
@ -20,7 +23,7 @@ const SegmentSetModal: React.FC<kFProps> = ({
|
|||||||
const { isShowSegmentSetModal } = kFModel;
|
const { isShowSegmentSetModal } = kFModel;
|
||||||
const parserList = useSelectParserList();
|
const parserList = useSelectParserList();
|
||||||
|
|
||||||
useFetchParserList();
|
useFetchTenantInfo();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSelectedTag(parser_id);
|
setSelectedTag(parser_id);
|
||||||
@ -57,7 +60,7 @@ const SegmentSetModal: React.FC<kFProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title="Parser Type"
|
title="Category"
|
||||||
open={isShowSegmentSetModal}
|
open={isShowSegmentSetModal}
|
||||||
onOk={handleOk}
|
onOk={handleOk}
|
||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
import {
|
import {
|
||||||
useFetchKnowledgeBaseConfiguration,
|
useFetchKnowledgeBaseConfiguration,
|
||||||
useFetchParserList,
|
|
||||||
useKnowledgeBaseId,
|
useKnowledgeBaseId,
|
||||||
useSelectParserList,
|
|
||||||
} from '@/hooks/knowledgeHook';
|
} from '@/hooks/knowledgeHook';
|
||||||
|
import {
|
||||||
|
useFetchTenantInfo,
|
||||||
|
useSelectParserList,
|
||||||
|
} from '@/hooks/userSettingHook';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Divider,
|
Divider,
|
||||||
@ -93,7 +96,7 @@ const Configuration = () => {
|
|||||||
});
|
});
|
||||||
}, [form, knowledgeDetails]);
|
}, [form, knowledgeDetails]);
|
||||||
|
|
||||||
useFetchParserList();
|
useFetchTenantInfo();
|
||||||
useFetchKnowledgeBaseConfiguration();
|
useFetchKnowledgeBaseConfiguration();
|
||||||
|
|
||||||
useFetchLlmList(LlmModelType.Embedding);
|
useFetchLlmList(LlmModelType.Embedding);
|
||||||
|
|||||||
@ -1,165 +1,3 @@
|
|||||||
import { KnowledgeRouteKey } from '@/constants/knowledge';
|
|
||||||
import { useKnowledgeBaseId } from '@/hooks/knowledgeHook';
|
|
||||||
import { Button, Form, Input, Radio, Select, Space, Tag } from 'antd';
|
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
|
||||||
import { useDispatch, useNavigate, useSelector } from 'umi';
|
|
||||||
import Configuration from './configuration';
|
import Configuration from './configuration';
|
||||||
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
const { CheckableTag } = Tag;
|
|
||||||
const layout = {
|
|
||||||
labelCol: { span: 8 },
|
|
||||||
wrapperCol: { span: 16 },
|
|
||||||
labelAlign: 'left' as const,
|
|
||||||
};
|
|
||||||
const { Option } = Select;
|
|
||||||
|
|
||||||
const KnowledgeSetting = () => {
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const settingModel = useSelector((state: any) => state.settingModel);
|
|
||||||
let navigate = useNavigate();
|
|
||||||
const { tenantIfo = {} } = settingModel;
|
|
||||||
const parser_ids = tenantIfo?.parser_ids ?? '';
|
|
||||||
const embd_id = tenantIfo?.embd_id ?? '';
|
|
||||||
const [form] = Form.useForm();
|
|
||||||
const [selectedTag, setSelectedTag] = useState('');
|
|
||||||
const values = Form.useWatch([], form);
|
|
||||||
const knowledgeBaseId = useKnowledgeBaseId();
|
|
||||||
|
|
||||||
const getTenantInfo = useCallback(async () => {
|
|
||||||
dispatch({
|
|
||||||
type: 'settingModel/getTenantInfo',
|
|
||||||
payload: {},
|
|
||||||
});
|
|
||||||
if (knowledgeBaseId) {
|
|
||||||
const data = await dispatch<any>({
|
|
||||||
type: 'kSModel/getKbDetail',
|
|
||||||
payload: {
|
|
||||||
kb_id: knowledgeBaseId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (data.retcode === 0) {
|
|
||||||
const { description, name, permission, embd_id } = data.data;
|
|
||||||
form.setFieldsValue({ description, name, permission, embd_id });
|
|
||||||
setSelectedTag(data.data.parser_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [knowledgeBaseId, dispatch, form]);
|
|
||||||
|
|
||||||
const onFinish = async () => {
|
|
||||||
try {
|
|
||||||
await form.validateFields();
|
|
||||||
|
|
||||||
if (knowledgeBaseId) {
|
|
||||||
dispatch({
|
|
||||||
type: 'kSModel/updateKb',
|
|
||||||
payload: {
|
|
||||||
...values,
|
|
||||||
parser_id: selectedTag,
|
|
||||||
kb_id: knowledgeBaseId,
|
|
||||||
embd_id: undefined,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const retcode = await dispatch<any>({
|
|
||||||
type: 'kSModel/createKb',
|
|
||||||
payload: {
|
|
||||||
...values,
|
|
||||||
parser_id: selectedTag,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (retcode === 0) {
|
|
||||||
navigate(
|
|
||||||
`/knowledge/${KnowledgeRouteKey.Dataset}?id=${knowledgeBaseId}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getTenantInfo();
|
|
||||||
}, [getTenantInfo]);
|
|
||||||
|
|
||||||
const handleChange = (tag: string, checked: boolean) => {
|
|
||||||
const nextSelectedTag = checked ? tag : selectedTag;
|
|
||||||
console.log('You are interested in: ', nextSelectedTag);
|
|
||||||
setSelectedTag(nextSelectedTag);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form
|
|
||||||
{...layout}
|
|
||||||
form={form}
|
|
||||||
name="validateOnly"
|
|
||||||
style={{ maxWidth: 1000, padding: 14 }}
|
|
||||||
>
|
|
||||||
<Form.Item name="name" label="知识库名称" rules={[{ required: true }]}>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="description" label="知识库描述">
|
|
||||||
<Input.TextArea />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="permission" label="可见权限">
|
|
||||||
<Radio.Group>
|
|
||||||
<Radio value="me">只有我</Radio>
|
|
||||||
<Radio value="team">所有团队成员</Radio>
|
|
||||||
</Radio.Group>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name="embd_id"
|
|
||||||
label="Embedding 模型"
|
|
||||||
hasFeedback
|
|
||||||
rules={[{ required: true, message: 'Please select your country!' }]}
|
|
||||||
>
|
|
||||||
<Select placeholder="Please select a country">
|
|
||||||
{embd_id.split(',').map((item: string) => {
|
|
||||||
return (
|
|
||||||
<Option value={item} key={item}>
|
|
||||||
{item}
|
|
||||||
</Option>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
<div style={{ marginTop: '5px' }}>
|
|
||||||
修改Embedding 模型,请去<span style={{ color: '#1677ff' }}>设置</span>
|
|
||||||
</div>
|
|
||||||
<Space size={[0, 8]} wrap>
|
|
||||||
<div className={styles.tags}>
|
|
||||||
{parser_ids.split(',').map((tag: string) => {
|
|
||||||
return (
|
|
||||||
<CheckableTag
|
|
||||||
key={tag}
|
|
||||||
checked={selectedTag === tag}
|
|
||||||
onChange={(checked) => handleChange(tag, checked)}
|
|
||||||
>
|
|
||||||
{tag}
|
|
||||||
</CheckableTag>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</Space>
|
|
||||||
<Space size={[0, 8]} wrap></Space>
|
|
||||||
<div className={styles.preset}>
|
|
||||||
<div className={styles.left}>xxxxx文章</div>
|
|
||||||
<div className={styles.right}>预估份数</div>
|
|
||||||
</div>
|
|
||||||
<Form.Item wrapperCol={{ ...layout.wrapperCol, offset: 8 }}>
|
|
||||||
<Button type="primary" onClick={onFinish}>
|
|
||||||
保存并处理
|
|
||||||
</Button>
|
|
||||||
<Button htmlType="button" style={{ marginLeft: '20px' }}>
|
|
||||||
取消
|
|
||||||
</Button>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// export default KnowledgeSetting;
|
|
||||||
|
|
||||||
export default Configuration;
|
export default Configuration;
|
||||||
|
|||||||
18
web/src/pages/chat/chat-configuration-modal/hooks.ts
Normal file
18
web/src/pages/chat/chat-configuration-modal/hooks.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import {
|
||||||
|
useFetchTenantInfo,
|
||||||
|
useSelectTenantInfo,
|
||||||
|
} from '@/hooks/userSettingHook';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
export const useFetchModelId = (visible: boolean) => {
|
||||||
|
const fetchTenantInfo = useFetchTenantInfo(false);
|
||||||
|
const tenantInfo = useSelectTenantInfo();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (visible) {
|
||||||
|
fetchTenantInfo();
|
||||||
|
}
|
||||||
|
}, [visible, fetchTenantInfo]);
|
||||||
|
|
||||||
|
return tenantInfo?.llm_id ?? '';
|
||||||
|
};
|
||||||
@ -13,6 +13,7 @@ import { variableEnabledFieldMap } from '../constants';
|
|||||||
import { useFetchDialog, useResetCurrentDialog, useSetDialog } from '../hooks';
|
import { useFetchDialog, useResetCurrentDialog, useSetDialog } from '../hooks';
|
||||||
import { IPromptConfigParameters } from '../interface';
|
import { IPromptConfigParameters } from '../interface';
|
||||||
import { excludeUnEnabledVariables } from '../utils';
|
import { excludeUnEnabledVariables } from '../utils';
|
||||||
|
import { useFetchModelId } from './hooks';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
enum ConfigurationSegmented {
|
enum ConfigurationSegmented {
|
||||||
@ -54,6 +55,7 @@ const ChatConfigurationModal = ({ visible, hideModal, id }: IProps) => {
|
|||||||
);
|
);
|
||||||
const promptEngineRef = useRef<Array<IPromptConfigParameters>>([]);
|
const promptEngineRef = useRef<Array<IPromptConfigParameters>>([]);
|
||||||
const loading = useOneNamespaceEffectsLoading('chatModel', ['setDialog']);
|
const loading = useOneNamespaceEffectsLoading('chatModel', ['setDialog']);
|
||||||
|
const modelId = useFetchModelId(visible);
|
||||||
|
|
||||||
const setDialog = useSetDialog();
|
const setDialog = useSetDialog();
|
||||||
const currentDialog = useFetchDialog(id, visible);
|
const currentDialog = useFetchDialog(id, visible);
|
||||||
@ -128,9 +130,13 @@ const ChatConfigurationModal = ({ visible, hideModal, id }: IProps) => {
|
|||||||
if (icon) {
|
if (icon) {
|
||||||
fileList = [{ uid: '1', name: 'file', thumbUrl: icon, status: 'done' }];
|
fileList = [{ uid: '1', name: 'file', thumbUrl: icon, status: 'done' }];
|
||||||
}
|
}
|
||||||
form.setFieldsValue({ ...currentDialog, icon: fileList });
|
form.setFieldsValue({
|
||||||
|
...currentDialog,
|
||||||
|
icon: fileList,
|
||||||
|
llm_id: currentDialog.llm_id ?? modelId,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, [currentDialog, form, visible]);
|
}, [currentDialog, form, visible, modelId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import omit from 'lodash/omit';
|
|||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useDispatch, useSearchParams, useSelector } from 'umi';
|
import { useDispatch, useSearchParams, useSelector } from 'umi';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import { ChatSearchParams, EmptyConversationId } from './constants';
|
import { ChatSearchParams } from './constants';
|
||||||
import {
|
import {
|
||||||
IClientConversation,
|
IClientConversation,
|
||||||
IMessage,
|
IMessage,
|
||||||
@ -233,75 +233,6 @@ export const useHandleItemHover = () => {
|
|||||||
|
|
||||||
//#region conversation
|
//#region conversation
|
||||||
|
|
||||||
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 = () => {
|
export const useFetchConversationList = () => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const conversationList: any[] = useSelector(
|
const conversationList: any[] = useSelector(
|
||||||
@ -412,7 +343,7 @@ export const useSelectCurrentConversation = () => {
|
|||||||
(state: any) => state.chatModel.currentConversation,
|
(state: any) => state.chatModel.currentConversation,
|
||||||
);
|
);
|
||||||
const dialog = useSelectCurrentDialog();
|
const dialog = useSelectCurrentDialog();
|
||||||
const { conversationId } = useGetChatSearchParams();
|
const { conversationId, dialogId } = useGetChatSearchParams();
|
||||||
|
|
||||||
const addNewestConversation = useCallback((message: string) => {
|
const addNewestConversation = useCallback((message: string) => {
|
||||||
setCurrentConversation((pre) => {
|
setCurrentConversation((pre) => {
|
||||||
@ -448,12 +379,12 @@ export const useSelectCurrentConversation = () => {
|
|||||||
|
|
||||||
setCurrentConversation({
|
setCurrentConversation({
|
||||||
id: '',
|
id: '',
|
||||||
dialog_id: dialog.id,
|
dialog_id: dialogId,
|
||||||
reference: [],
|
reference: [],
|
||||||
message: [nextMessage],
|
message: [nextMessage],
|
||||||
} as any);
|
} as any);
|
||||||
}
|
}
|
||||||
}, [conversationId, dialog]);
|
}, [conversationId, dialog, dialogId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
addPrologue();
|
addPrologue();
|
||||||
|
|||||||
@ -1,14 +1,17 @@
|
|||||||
|
import { IKnowledge } from '@/interfaces/database/knowledge';
|
||||||
import kbService from '@/services/kbService';
|
import kbService from '@/services/kbService';
|
||||||
import { DvaModel } from 'umi';
|
import { DvaModel } from 'umi';
|
||||||
|
|
||||||
export interface KnowledgeModelState {
|
export interface KnowledgeModelState {
|
||||||
data: any[];
|
data: any[];
|
||||||
|
knowledge: IKnowledge;
|
||||||
}
|
}
|
||||||
|
|
||||||
const model: DvaModel<KnowledgeModelState> = {
|
const model: DvaModel<KnowledgeModelState> = {
|
||||||
namespace: 'knowledgeModel',
|
namespace: 'knowledgeModel',
|
||||||
state: {
|
state: {
|
||||||
data: [],
|
data: [],
|
||||||
|
knowledge: {} as IKnowledge,
|
||||||
},
|
},
|
||||||
reducers: {
|
reducers: {
|
||||||
updateState(state, { payload }) {
|
updateState(state, { payload }) {
|
||||||
@ -17,6 +20,12 @@ const model: DvaModel<KnowledgeModelState> = {
|
|||||||
...payload,
|
...payload,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
setKnowledge(state, { payload }) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
knowledge: payload,
|
||||||
|
};
|
||||||
|
},
|
||||||
},
|
},
|
||||||
effects: {
|
effects: {
|
||||||
*rmKb({ payload = {} }, { call, put }) {
|
*rmKb({ payload = {} }, { call, put }) {
|
||||||
@ -42,6 +51,13 @@ const model: DvaModel<KnowledgeModelState> = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
*getKnowledgeDetail({ payload = {} }, { call, put }) {
|
||||||
|
const { data } = yield call(kbService.get_kb_detail, payload);
|
||||||
|
if (data.retcode === 0) {
|
||||||
|
yield put({ type: 'setKnowledge', payload: data.data });
|
||||||
|
}
|
||||||
|
return data.retcode;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
export default model;
|
export default model;
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { IChunk } from '@/interfaces/database/knowledge';
|
import { IChunk } from '@/interfaces/database/knowledge';
|
||||||
|
import { UploadFile } from 'antd';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
export const buildChunkHighlights = (selectedChunk: IChunk) => {
|
export const buildChunkHighlights = (selectedChunk: IChunk) => {
|
||||||
@ -32,3 +33,5 @@ export const buildChunkHighlights = (selectedChunk: IChunk) => {
|
|||||||
})
|
})
|
||||||
: [];
|
: [];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isFileUploadDone = (file: UploadFile) => file.status === 'done';
|
||||||
|
|||||||
Reference in New Issue
Block a user