mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-08-16 07:55:54 +08:00
feat: remove KnowledgeSearching and add knowledge configuration page and add a run button to the document (#64)
* feat: add a run button to the document * feat: add knowledge configuration page * feat: remove KnowledgeSearching
This commit is contained in:
parent
53be70c7a9
commit
f3d0ebd293
5
web/src/assets/svg/refresh.svg
Normal file
5
web/src/assets/svg/refresh.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M14.1667 4.27108C15.9345 5.55903 17.0834 7.6453 17.0834 9.99992C17.0834 13.9119 13.9121 17.0833 10.0001 17.0833H9.58342M5.83341 15.7288C4.06564 14.4408 2.91675 12.3545 2.91675 9.99992C2.91675 6.0879 6.08806 2.91659 10.0001 2.91659H10.4167M10.8334 18.6666L9.16675 16.9999L10.8334 15.3333M9.16675 4.66659L10.8334 2.99992L9.16675 1.33325"
|
||||
stroke="#17B26A" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
After Width: | Height: | Size: 557 B |
15
web/src/assets/svg/run.svg
Normal file
15
web/src/assets/svg/run.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_490_5128)">
|
||||
<path
|
||||
d="M10.0001 18.3334C14.6025 18.3334 18.3334 14.6025 18.3334 10.0001C18.3334 5.39771 14.6025 1.66675 10.0001 1.66675C5.39771 1.66675 1.66675 5.39771 1.66675 10.0001C1.66675 14.6025 5.39771 18.3334 10.0001 18.3334Z"
|
||||
stroke="#17B26A" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path
|
||||
d="M7.91675 7.47119C7.91675 7.07345 7.91675 6.87459 7.99987 6.76356C8.0723 6.66681 8.18318 6.60628 8.30373 6.59767C8.44207 6.58779 8.60935 6.69533 8.94392 6.91041L12.8777 9.4393C13.1681 9.62593 13.3132 9.71925 13.3634 9.83791C13.4072 9.94159 13.4072 10.0586 13.3634 10.1623C13.3132 10.2809 13.1681 10.3742 12.8777 10.5609L8.94392 13.0898C8.60935 13.3048 8.44207 13.4124 8.30373 13.4025C8.18318 13.3939 8.0723 13.3334 7.99987 13.2366C7.91675 13.1256 7.91675 12.9267 7.91675 12.529V7.47119Z"
|
||||
stroke="#17B26A" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_490_5128">
|
||||
<rect width="20" height="20" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
@ -2,13 +2,12 @@ export enum KnowledgeRouteKey {
|
||||
Dataset = 'dataset',
|
||||
Testing = 'testing',
|
||||
Configuration = 'configuration',
|
||||
TempTesting = 'tempTesting',
|
||||
}
|
||||
|
||||
export enum RunningStatus {
|
||||
UNSTART = '0',
|
||||
RUNNING = '1',
|
||||
CANCEL = '2',
|
||||
DONE = '3',
|
||||
FAIL = '4',
|
||||
UNSTART = '0', // need to run
|
||||
RUNNING = '1', // need to cancel
|
||||
CANCEL = '2', // need to refresh
|
||||
DONE = '3', // need to refresh
|
||||
FAIL = '4', // need to refresh
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import showDeleteConfirm from '@/components/deleting-confirm';
|
||||
import { IKnowledge } from '@/interfaces/database/knowledge';
|
||||
import { useCallback } from 'react';
|
||||
import { IKnowledge, ITenantInfo } from '@/interfaces/database/knowledge';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useDispatch, useSearchParams, useSelector } from 'umi';
|
||||
|
||||
export const useKnowledgeBaseId = (): string => {
|
||||
@ -77,3 +77,50 @@ export const useDeleteChunkByIds = (): {
|
||||
removeChunk: onRemoveChunk,
|
||||
};
|
||||
};
|
||||
|
||||
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 = () => {
|
||||
const dispatch = useDispatch();
|
||||
const knowledgeBaseId = useKnowledgeBaseId();
|
||||
|
||||
const fetchKnowledgeBaseConfiguration = useCallback(() => {
|
||||
dispatch({
|
||||
type: 'kSModel/getKbDetail',
|
||||
payload: {
|
||||
kb_id: knowledgeBaseId,
|
||||
},
|
||||
});
|
||||
}, [dispatch, knowledgeBaseId]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchKnowledgeBaseConfiguration();
|
||||
}, [fetchKnowledgeBaseConfiguration]);
|
||||
};
|
||||
|
16
web/src/interfaces/database/llm.ts
Normal file
16
web/src/interfaces/database/llm.ts
Normal file
@ -0,0 +1,16 @@
|
||||
export interface IThirdOAIModel {
|
||||
available: boolean;
|
||||
create_date: string;
|
||||
create_time: number;
|
||||
fid: string;
|
||||
id: number;
|
||||
llm_name: string;
|
||||
max_tokens: number;
|
||||
model_type: string;
|
||||
status: string;
|
||||
tags: string;
|
||||
update_date: string;
|
||||
update_time: number;
|
||||
}
|
||||
|
||||
export type IThirdOAIModelCollection = Record<string, IThirdOAIModel[]>;
|
@ -26,6 +26,7 @@
|
||||
.chunkContainer {
|
||||
height: calc(100vh - 320px);
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pageFooter {
|
||||
|
@ -2,10 +2,11 @@ import { ReactComponent as SelectFilesEndIcon } from '@/assets/svg/select-files-
|
||||
import { ReactComponent as SelectFilesStartIcon } from '@/assets/svg/select-files-start.svg';
|
||||
import {
|
||||
useDeleteDocumentById,
|
||||
useFetchParserList,
|
||||
useGetDocumentDefaultParser,
|
||||
useKnowledgeBaseId,
|
||||
useSelectParserList,
|
||||
} from '@/hooks/knowledgeHook';
|
||||
import { ITenantInfo } from '@/interfaces/database/knowledge';
|
||||
import uploadService from '@/services/uploadService';
|
||||
import {
|
||||
ArrowLeftOutlined,
|
||||
@ -28,9 +29,8 @@ import {
|
||||
UploadProps,
|
||||
} from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import { ReactElement, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Nullable } from 'typings';
|
||||
import { Link, useDispatch, useNavigate, useSelector } from 'umi';
|
||||
import { ReactElement, useEffect, useRef, useState } from 'react';
|
||||
import { Link, useDispatch, useNavigate } from 'umi';
|
||||
|
||||
import { KnowledgeRouteKey } from '@/constants/knowledge';
|
||||
import styles from './index.less';
|
||||
@ -45,13 +45,11 @@ const UploaderItem = ({
|
||||
file,
|
||||
actions,
|
||||
isUpload,
|
||||
parserArray,
|
||||
}: {
|
||||
isUpload: boolean;
|
||||
originNode: ReactElement;
|
||||
file: UploadFile;
|
||||
fileList: object[];
|
||||
parserArray: string[];
|
||||
actions: { download: Function; preview: Function; remove: any };
|
||||
}) => {
|
||||
const { parserConfig, defaultParserId } = useGetDocumentDefaultParser(
|
||||
@ -63,12 +61,7 @@ const UploaderItem = ({
|
||||
|
||||
const documentId = file?.response?.id;
|
||||
|
||||
const parserList = useMemo(() => {
|
||||
return parserArray.map((x) => {
|
||||
const arr = x.split(':');
|
||||
return { value: arr[0], label: arr[1] };
|
||||
});
|
||||
}, [parserArray]);
|
||||
const parserList = useSelectParserList();
|
||||
|
||||
const saveParser = (parserId: string) => {
|
||||
dispatch({
|
||||
@ -154,14 +147,10 @@ const KnowledgeUploadFile = () => {
|
||||
const knowledgeBaseId = useKnowledgeBaseId();
|
||||
const [isUpload, setIsUpload] = useState(true);
|
||||
const dispatch = useDispatch();
|
||||
const tenantIfo: Nullable<ITenantInfo> = useSelector(
|
||||
(state: any) => state.settingModel.tenantIfo,
|
||||
);
|
||||
|
||||
const navigate = useNavigate();
|
||||
const fileListRef = useRef<UploadFile[]>([]);
|
||||
|
||||
const parserArray = tenantIfo?.parser_ids.split(',') ?? [];
|
||||
|
||||
const createRequest: (props: UploadRequestOption) => void = async function ({
|
||||
file,
|
||||
onSuccess,
|
||||
@ -170,9 +159,13 @@ const KnowledgeUploadFile = () => {
|
||||
}) {
|
||||
const { data } = await uploadService.uploadFile(file, knowledgeBaseId);
|
||||
if (data.retcode === 0) {
|
||||
onSuccess && onSuccess(data.data);
|
||||
if (onSuccess) {
|
||||
onSuccess(data.data);
|
||||
}
|
||||
} else {
|
||||
onError && onError(data.data);
|
||||
if (onError) {
|
||||
onError(data.data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -188,7 +181,6 @@ const KnowledgeUploadFile = () => {
|
||||
fileList={fileList}
|
||||
originNode={originNode}
|
||||
actions={actions}
|
||||
parserArray={parserArray}
|
||||
></UploaderItem>
|
||||
);
|
||||
},
|
||||
@ -215,11 +207,7 @@ const KnowledgeUploadFile = () => {
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
dispatch({
|
||||
type: 'settingModel/getTenantInfo',
|
||||
});
|
||||
}, []);
|
||||
useFetchParserList();
|
||||
|
||||
return (
|
||||
<div className={styles.uploadWrapper}>
|
||||
|
@ -1,8 +1,5 @@
|
||||
import { KnowledgeRouteKey } from '@/constants/knowledge';
|
||||
import {
|
||||
useDeleteDocumentById,
|
||||
useKnowledgeBaseId,
|
||||
} from '@/hooks/knowledgeHook';
|
||||
import { useKnowledgeBaseId } from '@/hooks/knowledgeHook';
|
||||
import { Pagination } from '@/interfaces/common';
|
||||
import { IKnowledgeFile } from '@/interfaces/database/knowledge';
|
||||
import { getOneNamespaceEffectsLoading } from '@/utils/storeUtil';
|
||||
@ -40,7 +37,6 @@ const KnowledgeFile = () => {
|
||||
const effects = useSelector((state: any) => state.loading.effects);
|
||||
const { data, total } = kFModel;
|
||||
const knowledgeBaseId = useKnowledgeBaseId();
|
||||
const { removeDocument } = useDeleteDocumentById();
|
||||
|
||||
const loading = getOneNamespaceEffectsLoading('kFModel', effects, [
|
||||
'getKfList',
|
||||
@ -132,9 +128,7 @@ const KnowledgeFile = () => {
|
||||
},
|
||||
});
|
||||
};
|
||||
const onRmDocument = () => {
|
||||
removeDocument(doc_id);
|
||||
};
|
||||
|
||||
const showCEFModal = () => {
|
||||
dispatch({
|
||||
type: 'kFModel/updateState',
|
||||
@ -144,15 +138,6 @@ const KnowledgeFile = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const showSegmentSetModal = () => {
|
||||
dispatch({
|
||||
type: 'kFModel/updateState',
|
||||
payload: {
|
||||
isShowSegmentSetModal: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const actionItems: MenuProps['items'] = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
@ -185,31 +170,6 @@ const KnowledgeFile = () => {
|
||||
},
|
||||
];
|
||||
}, []);
|
||||
const chunkItems: MenuProps['items'] = [
|
||||
{
|
||||
key: '1',
|
||||
label: (
|
||||
<div>
|
||||
<Button type="link" onClick={showSegmentSetModal}>
|
||||
{' '}
|
||||
分段设置
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
label: (
|
||||
<div>
|
||||
<Button type="link" onClick={onRmDocument}>
|
||||
{' '}
|
||||
Delete
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
// disabled: true,
|
||||
},
|
||||
];
|
||||
|
||||
const toChunk = (id: string) => {
|
||||
navigate(
|
||||
|
@ -55,33 +55,23 @@ const model: DvaModel<KFModelState> = {
|
||||
return { ...state, pagination: { ...state.pagination, ...payload } };
|
||||
},
|
||||
},
|
||||
subscriptions: {
|
||||
setup({ dispatch, history }) {
|
||||
history.listen((location) => {});
|
||||
},
|
||||
},
|
||||
effects: {
|
||||
*createKf({ payload = {} }, { call, put }) {
|
||||
const { data, response } = yield call(kbService.createKb, payload);
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
*createKf({ payload = {} }, { call }) {
|
||||
const { data } = yield call(kbService.createKb, payload);
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
message.success('创建成功!');
|
||||
}
|
||||
},
|
||||
*updateKf({ payload = {} }, { call, put }) {
|
||||
const { data, response } = yield call(kbService.updateKb, payload);
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
*updateKf({ payload = {} }, { call }) {
|
||||
const { data } = yield call(kbService.updateKb, payload);
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
message.success('修改成功!');
|
||||
}
|
||||
},
|
||||
*getKfDetail({ payload = {}, callback }, { call, put }) {
|
||||
const { data, response } = yield call(kbService.get_kb_detail, payload);
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
if (retcode === 0) {
|
||||
// localStorage.setItem('userInfo',res.)
|
||||
callback && callback(res);
|
||||
}
|
||||
*getKfDetail({ payload = {} }, { call }) {
|
||||
const { data } = yield call(kbService.get_kb_detail, payload);
|
||||
},
|
||||
*getKfList({ payload = {} }, { call, put, select }) {
|
||||
const state: KFModelState = yield select((state: any) => state.kFModel);
|
||||
@ -119,11 +109,11 @@ const model: DvaModel<KFModelState> = {
|
||||
{ type: 'poll', delay: 5000 }, // TODO: Provide type support for this effect
|
||||
],
|
||||
*updateDocumentStatus({ payload = {} }, { call, put }) {
|
||||
const { data, response } = yield call(
|
||||
const { data } = yield call(
|
||||
kbService.document_change_status,
|
||||
pick(payload, ['doc_id', 'status']),
|
||||
);
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
message.success('修改成功!');
|
||||
put({
|
||||
@ -133,10 +123,10 @@ const model: DvaModel<KFModelState> = {
|
||||
}
|
||||
},
|
||||
*document_rm({ payload = {} }, { call, put }) {
|
||||
const { data, response } = yield call(kbService.document_rm, {
|
||||
const { data } = yield call(kbService.document_rm, {
|
||||
doc_id: payload.doc_id,
|
||||
});
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
message.success('删除成功!');
|
||||
yield put({
|
||||
@ -151,7 +141,7 @@ const model: DvaModel<KFModelState> = {
|
||||
kbService.document_rename,
|
||||
omit(payload, ['kb_id']),
|
||||
);
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
message.success('rename success!');
|
||||
yield put({
|
||||
@ -168,7 +158,7 @@ const model: DvaModel<KFModelState> = {
|
||||
},
|
||||
*document_create({ payload = {} }, { call, put }) {
|
||||
const { data } = yield call(kbService.document_create, payload);
|
||||
const { retcode, data: res } = data;
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
put({
|
||||
type: 'kFModel/updateState',
|
||||
@ -181,19 +171,25 @@ const model: DvaModel<KFModelState> = {
|
||||
return retcode;
|
||||
},
|
||||
*document_run({ payload = {} }, { call, put }) {
|
||||
const { data } = yield call(kbService.document_run, payload);
|
||||
const { data } = yield call(
|
||||
kbService.document_run,
|
||||
omit(payload, ['knowledgeBaseId']),
|
||||
);
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
message.success('Run successfully !');
|
||||
if (payload.knowledgeBaseId) {
|
||||
yield put({
|
||||
type: 'getKfList',
|
||||
payload: { kb_id: payload.knowledgeBaseId },
|
||||
});
|
||||
}
|
||||
message.success('Operation successfully !');
|
||||
}
|
||||
return retcode;
|
||||
},
|
||||
*document_change_parser({ payload = {} }, { call, put }) {
|
||||
const { data, response } = yield call(
|
||||
kbService.document_change_parser,
|
||||
payload,
|
||||
);
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
const { data } = yield call(kbService.document_change_parser, payload);
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
put({
|
||||
type: 'updateState',
|
||||
|
@ -1,3 +1,12 @@
|
||||
.popover-content {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.operationIcon {
|
||||
text-align: center;
|
||||
margin-right: 20%;
|
||||
width: 20px;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,21 @@
|
||||
import { ReactComponent as RefreshIcon } from '@/assets/svg/refresh.svg';
|
||||
import { ReactComponent as RunIcon } from '@/assets/svg/run.svg';
|
||||
import { IKnowledgeFile } from '@/interfaces/database/knowledge';
|
||||
import { Badge, DescriptionsProps, Flex, Popover, Space, Tag } from 'antd';
|
||||
import { RunningStatus, RunningStatusMap } from '../constant';
|
||||
|
||||
import { CloseCircleOutlined } from '@ant-design/icons';
|
||||
import { useDispatch } from 'umi';
|
||||
import styles from './index.less';
|
||||
|
||||
const iconMap = {
|
||||
[RunningStatus.UNSTART]: RunIcon,
|
||||
[RunningStatus.RUNNING]: CloseCircleOutlined,
|
||||
[RunningStatus.CANCEL]: RefreshIcon,
|
||||
[RunningStatus.DONE]: RefreshIcon,
|
||||
[RunningStatus.FAIL]: RefreshIcon,
|
||||
};
|
||||
|
||||
interface IProps {
|
||||
record: IKnowledgeFile;
|
||||
}
|
||||
@ -31,7 +43,7 @@ const PopoverContent = ({ record }: IProps) => {
|
||||
<Flex vertical className={styles['popover-content']}>
|
||||
{items.map((x) => {
|
||||
return (
|
||||
<div>
|
||||
<div key={x.key}>
|
||||
<b>{x.label}:</b>
|
||||
<p>{x.children}</p>
|
||||
</div>
|
||||
@ -42,12 +54,27 @@ const PopoverContent = ({ record }: IProps) => {
|
||||
};
|
||||
|
||||
export const ParsingStatusCell = ({ record }: IProps) => {
|
||||
const dispatch = useDispatch();
|
||||
const text = record.run;
|
||||
const runningStatus = RunningStatusMap[text];
|
||||
|
||||
const isRunning = text === RunningStatus.RUNNING;
|
||||
|
||||
const OperationIcon = iconMap[text];
|
||||
|
||||
const handleOperationIconClick = () => {
|
||||
dispatch({
|
||||
type: 'kFModel/document_run',
|
||||
payload: {
|
||||
doc_ids: [record.id],
|
||||
run: isRunning ? 2 : 1,
|
||||
knowledgeBaseId: record.kb_id,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Flex justify={'space-between'}>
|
||||
<Popover
|
||||
content={isRunning && <PopoverContent record={record}></PopoverContent>}
|
||||
>
|
||||
@ -56,13 +83,17 @@ export const ParsingStatusCell = ({ record }: IProps) => {
|
||||
<Space>
|
||||
<Badge color={runningStatus.color} />
|
||||
{runningStatus.label}
|
||||
<span>{record.progress * 100}%</span>
|
||||
<span>{(record.progress * 100).toFixed(2)}%</span>
|
||||
</Space>
|
||||
) : (
|
||||
runningStatus.label
|
||||
)}
|
||||
</Tag>
|
||||
</Popover>
|
||||
<div onClick={handleOperationIconClick} className={styles.operationIcon}>
|
||||
<OperationIcon />
|
||||
</div>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,79 +0,0 @@
|
||||
.chunkPage {
|
||||
padding: 24px;
|
||||
display: flex;
|
||||
height: calc(100vh - 112px);
|
||||
// flex-direction: column;
|
||||
|
||||
.filter {
|
||||
margin-right: 20px;
|
||||
display: flex;
|
||||
height: 32px;
|
||||
width: 300px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.pageContainer {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.pageContent {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
padding-right: 12px;
|
||||
overflow-y: auto;
|
||||
|
||||
.spin {
|
||||
min-height: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
.pageFooter {
|
||||
height: 32px;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.container {
|
||||
height: 100px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.context {
|
||||
flex: 1;
|
||||
// width: 207px;
|
||||
height: 88px;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
height: 20px;
|
||||
|
||||
.text {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
:global {
|
||||
.ant-card-body {
|
||||
padding: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
}
|
@ -1,276 +0,0 @@
|
||||
import { useKnowledgeBaseId } from '@/hooks/knowledgeHook';
|
||||
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
||||
import { api_host } from '@/utils/api';
|
||||
import { DeleteOutlined, MinusSquareOutlined } from '@ant-design/icons';
|
||||
import type { PaginationProps } from 'antd';
|
||||
import {
|
||||
Card,
|
||||
Col,
|
||||
Input,
|
||||
Pagination,
|
||||
Popconfirm,
|
||||
Row,
|
||||
Select,
|
||||
Spin,
|
||||
Switch,
|
||||
} from 'antd';
|
||||
import { debounce } from 'lodash';
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'umi';
|
||||
import CreateModal from '../knowledge-chunk/components/chunk-creating-modal';
|
||||
|
||||
import styles from './index.less';
|
||||
|
||||
const KnowledgeSearching = () => {
|
||||
const dispatch = useDispatch();
|
||||
const kSearchModel = useSelector((state: any) => state.kSearchModel);
|
||||
const chunkModel = useSelector((state: any) => state.chunkModel);
|
||||
const loading = useOneNamespaceEffectsLoading('kSearchModel', [
|
||||
'chunk_list',
|
||||
'switch_chunk',
|
||||
]);
|
||||
const knowledgeBaseId = useKnowledgeBaseId();
|
||||
|
||||
const {
|
||||
data = [],
|
||||
total,
|
||||
d_list = [],
|
||||
question,
|
||||
doc_ids,
|
||||
pagination,
|
||||
} = kSearchModel;
|
||||
const { chunk_id, doc_id, isShowCreateModal } = chunkModel;
|
||||
|
||||
const getChunkList = () => {
|
||||
dispatch({
|
||||
type: 'kSearchModel/chunk_list',
|
||||
payload: {
|
||||
kb_id: knowledgeBaseId,
|
||||
},
|
||||
});
|
||||
};
|
||||
const confirm = (id: string) => {
|
||||
dispatch({
|
||||
type: 'kSearchModel/rm_chunk',
|
||||
payload: {
|
||||
chunk_ids: [id],
|
||||
kb_id: knowledgeBaseId,
|
||||
},
|
||||
});
|
||||
};
|
||||
const handleEditchunk = (item: any) => {
|
||||
const { chunk_id, doc_id } = item;
|
||||
dispatch({
|
||||
type: 'chunkModel/updateState',
|
||||
payload: {
|
||||
isShowCreateModal: true,
|
||||
chunk_id,
|
||||
doc_id,
|
||||
},
|
||||
});
|
||||
getChunkList();
|
||||
};
|
||||
const onShowSizeChange: PaginationProps['onShowSizeChange'] = (
|
||||
page,
|
||||
size,
|
||||
) => {
|
||||
dispatch({
|
||||
type: 'kSearchModel/updateState',
|
||||
payload: {
|
||||
pagination: { page, size },
|
||||
},
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
dispatch({
|
||||
type: 'kSearchModel/updateState',
|
||||
payload: {
|
||||
doc_ids: [],
|
||||
question: '',
|
||||
},
|
||||
});
|
||||
dispatch({
|
||||
type: 'kSearchModel/getKfList',
|
||||
payload: {
|
||||
kb_id: knowledgeBaseId,
|
||||
},
|
||||
});
|
||||
}, []);
|
||||
const switchChunk = (item: any, available_int: boolean) => {
|
||||
const { chunk_id, doc_id } = item;
|
||||
|
||||
dispatch({
|
||||
type: 'kSearchModel/switch_chunk',
|
||||
payload: {
|
||||
chunk_ids: [chunk_id],
|
||||
doc_id,
|
||||
available_int,
|
||||
kb_id: knowledgeBaseId,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getChunkList();
|
||||
}, [doc_ids, pagination, question]);
|
||||
const debounceChange = debounce((value) => {
|
||||
dispatch({
|
||||
type: 'kSearchModel/updateState',
|
||||
payload: {
|
||||
question: value,
|
||||
},
|
||||
});
|
||||
}, 300);
|
||||
|
||||
const debounceCallback = useCallback(
|
||||
(value: string) => debounceChange(value),
|
||||
[],
|
||||
);
|
||||
const handleInputChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||
) => {
|
||||
const value = e.target.value;
|
||||
debounceCallback(value);
|
||||
};
|
||||
const handleSelectChange = (value: any[]) => {
|
||||
dispatch({
|
||||
type: 'kSearchModel/updateState',
|
||||
payload: {
|
||||
doc_ids: value,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.chunkPage}>
|
||||
<div className={styles.filter}>
|
||||
<Select
|
||||
showSearch
|
||||
placeholder="文件列表"
|
||||
optionFilterProp="children"
|
||||
onChange={handleSelectChange}
|
||||
style={{ width: 300, marginBottom: 20 }}
|
||||
options={d_list}
|
||||
fieldNames={{ label: 'name', value: 'id' }}
|
||||
mode="multiple"
|
||||
/>
|
||||
|
||||
<Input.TextArea
|
||||
autoSize={{ minRows: 6, maxRows: 6 }}
|
||||
placeholder="搜索"
|
||||
style={{ width: 300 }}
|
||||
allowClear
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.pageContainer}>
|
||||
<div className={styles.pageContent}>
|
||||
<Spin spinning={loading} className={styles.spin} size="large">
|
||||
<Row gutter={{ xs: 8, sm: 16, md: 24, lg: 24 }}>
|
||||
{data.map((item: any) => {
|
||||
return (
|
||||
<Col
|
||||
className="gutter-row"
|
||||
key={item.chunk_id}
|
||||
xs={24}
|
||||
sm={12}
|
||||
md={12}
|
||||
lg={8}
|
||||
>
|
||||
<Card
|
||||
className={styles.card}
|
||||
onClick={() => {
|
||||
handleEditchunk(item);
|
||||
}}
|
||||
>
|
||||
<img
|
||||
style={{ width: '50px' }}
|
||||
src={`${api_host}/document/image/${item.img_id}`}
|
||||
alt=""
|
||||
/>
|
||||
<div className={styles.container}>
|
||||
<div className={styles.content}>
|
||||
<span className={styles.context}>
|
||||
{item.content_ltks}
|
||||
</span>
|
||||
<span className={styles.delete}>
|
||||
<Switch
|
||||
size="small"
|
||||
defaultValue={item.doc_ids == '1'}
|
||||
onChange={(checked: boolean, e: any) => {
|
||||
e.stopPropagation();
|
||||
e.nativeEvent.stopImmediatePropagation();
|
||||
switchChunk(item, checked);
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div className={styles.footer}>
|
||||
<span className={styles.text}>
|
||||
<MinusSquareOutlined />
|
||||
{item.doc_num}文档
|
||||
</span>
|
||||
<span className={styles.text}>
|
||||
<MinusSquareOutlined />
|
||||
{item.chunk_num}个
|
||||
</span>
|
||||
<span className={styles.text}>
|
||||
<MinusSquareOutlined />
|
||||
{item.token_num}千字符
|
||||
</span>
|
||||
<span style={{ float: 'right' }}>
|
||||
<Popconfirm
|
||||
title="Delete the task"
|
||||
description="Are you sure to delete this task?"
|
||||
onConfirm={(e: any) => {
|
||||
e.stopPropagation();
|
||||
e.nativeEvent.stopImmediatePropagation();
|
||||
console.log(confirm);
|
||||
confirm(item.chunk_id);
|
||||
}}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
>
|
||||
<DeleteOutlined
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.nativeEvent.stopImmediatePropagation();
|
||||
}}
|
||||
/>
|
||||
</Popconfirm>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
</Spin>
|
||||
</div>
|
||||
<div className={styles.pageFooter}>
|
||||
<Pagination
|
||||
responsive
|
||||
showLessItems
|
||||
showQuickJumper
|
||||
showSizeChanger
|
||||
onChange={onShowSizeChange}
|
||||
defaultPageSize={30}
|
||||
pageSizeOptions={[30, 60, 90]}
|
||||
defaultCurrent={pagination.page}
|
||||
total={total}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<CreateModal
|
||||
isShowCreateModal={isShowCreateModal}
|
||||
chunk_id={chunk_id}
|
||||
doc_id={doc_id}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default KnowledgeSearching;
|
@ -1,160 +0,0 @@
|
||||
import kbService from '@/services/kbService';
|
||||
import omit from 'lodash/omit';
|
||||
import { DvaModel } from 'umi';
|
||||
|
||||
export interface KSearchModelState {
|
||||
loading: boolean;
|
||||
data: any[];
|
||||
total: number;
|
||||
isShowCreateModal: boolean;
|
||||
chunk_id: string;
|
||||
chunkInfo: any;
|
||||
d_list: any[];
|
||||
question: string;
|
||||
doc_ids: any[];
|
||||
pagination: any;
|
||||
doc_id: string;
|
||||
}
|
||||
|
||||
const model: DvaModel<KSearchModelState> = {
|
||||
namespace: 'kSearchModel',
|
||||
state: {
|
||||
loading: false,
|
||||
data: [],
|
||||
total: 0,
|
||||
isShowCreateModal: false,
|
||||
chunk_id: '',
|
||||
chunkInfo: {},
|
||||
d_list: [],
|
||||
question: '',
|
||||
doc_ids: [],
|
||||
pagination: { page: 1, size: 30 },
|
||||
doc_id: '',
|
||||
},
|
||||
reducers: {
|
||||
updateState(state, { payload }) {
|
||||
return {
|
||||
...state,
|
||||
...payload,
|
||||
};
|
||||
},
|
||||
},
|
||||
subscriptions: {
|
||||
setup({ dispatch, history }) {
|
||||
history.listen((location) => {
|
||||
console.log(location);
|
||||
});
|
||||
},
|
||||
},
|
||||
effects: {
|
||||
*getKfList({ payload = {} }, { call, put }) {
|
||||
const { data, response } = yield call(
|
||||
kbService.get_document_list,
|
||||
payload,
|
||||
);
|
||||
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
if (retcode === 0) {
|
||||
yield put({
|
||||
type: 'updateState',
|
||||
payload: {
|
||||
d_list: res,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
*chunk_list({ payload = {} }, { call, put, select }) {
|
||||
const { question, doc_ids, pagination }: KSearchModelState = yield select(
|
||||
(state: any) => state.kSearchModel,
|
||||
);
|
||||
const { data } = yield call(kbService.retrieval_test, {
|
||||
...payload,
|
||||
...pagination,
|
||||
question,
|
||||
doc_ids,
|
||||
similarity_threshold: 0.1,
|
||||
});
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
if (retcode === 0) {
|
||||
yield put({
|
||||
type: 'updateState',
|
||||
payload: {
|
||||
data: res.chunks,
|
||||
total: res.total,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
*switch_chunk({ payload = {} }, { call, put }) {
|
||||
const { data } = yield call(
|
||||
kbService.switch_chunk,
|
||||
omit(payload, ['kb_id']),
|
||||
);
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
yield put({
|
||||
type: 'chunk_list',
|
||||
payload: {
|
||||
kb_id: payload.kb_id,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
*rm_chunk({ payload = {} }, { call, put }) {
|
||||
const { data } = yield call(kbService.rm_chunk, {
|
||||
chunk_ids: payload.chunk_ids,
|
||||
});
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
if (retcode === 0) {
|
||||
// TODO: Can be extracted
|
||||
yield put({
|
||||
type: 'chunk_list',
|
||||
payload: {
|
||||
kb_id: payload.kb_id,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
*get_chunk({ payload = {} }, { call, put }) {
|
||||
const { data, response } = yield call(kbService.get_chunk, payload);
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
if (retcode === 0) {
|
||||
yield put({
|
||||
type: 'updateState',
|
||||
payload: {
|
||||
chunkInfo: res,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
*create_hunk({ payload = {} }, { call, put }) {
|
||||
yield put({
|
||||
type: 'updateState',
|
||||
payload: {
|
||||
loading: true,
|
||||
},
|
||||
});
|
||||
let service = kbService.create_chunk;
|
||||
if (payload.chunk_id) {
|
||||
service = kbService.set_chunk;
|
||||
}
|
||||
const { data } = yield call(service, payload);
|
||||
const { retcode } = data;
|
||||
yield put({
|
||||
type: 'updateState',
|
||||
payload: {
|
||||
loading: false,
|
||||
},
|
||||
});
|
||||
if (retcode === 0) {
|
||||
yield put({
|
||||
type: 'updateState',
|
||||
payload: {
|
||||
isShowCreateModal: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
export default model;
|
@ -0,0 +1,217 @@
|
||||
import {
|
||||
useFetchKnowledgeBaseConfiguration,
|
||||
useFetchParserList,
|
||||
useKnowledgeBaseId,
|
||||
useSelectParserList,
|
||||
} from '@/hooks/knowledgeHook';
|
||||
import {
|
||||
Button,
|
||||
Divider,
|
||||
Form,
|
||||
Input,
|
||||
Radio,
|
||||
Select,
|
||||
Space,
|
||||
Typography,
|
||||
Upload,
|
||||
UploadFile,
|
||||
} from 'antd';
|
||||
import pick from 'lodash/pick';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useDispatch, useSelector } from 'umi';
|
||||
|
||||
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
||||
import { IKnowledge } from '@/interfaces/database/knowledge';
|
||||
import { IThirdOAIModelCollection } from '@/interfaces/database/llm';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import styles from './index.less';
|
||||
|
||||
const { Title } = Typography;
|
||||
const { Option } = Select;
|
||||
|
||||
const Configuration = () => {
|
||||
const [form] = Form.useForm();
|
||||
const dispatch = useDispatch();
|
||||
const knowledgeBaseId = useKnowledgeBaseId();
|
||||
const loading = useOneNamespaceEffectsLoading('kSModel', ['updateKb']);
|
||||
|
||||
const llmInfo: IThirdOAIModelCollection = useSelector(
|
||||
(state: any) => state.settingModel.llmInfo,
|
||||
);
|
||||
const knowledgeDetails: IKnowledge = useSelector(
|
||||
(state: any) => state.kSModel.knowledgeDetails,
|
||||
);
|
||||
|
||||
const normFile = (e: any) => {
|
||||
if (Array.isArray(e)) {
|
||||
return e;
|
||||
}
|
||||
return e?.fileList;
|
||||
};
|
||||
|
||||
const parserList = useSelectParserList();
|
||||
|
||||
const embeddingModelOptions = useMemo(() => {
|
||||
return Object.entries(llmInfo).map(([key, value]) => {
|
||||
return {
|
||||
label: key,
|
||||
options: value.map((x) => ({
|
||||
label: x.llm_name,
|
||||
value: x.llm_name,
|
||||
})),
|
||||
};
|
||||
});
|
||||
}, [llmInfo]);
|
||||
|
||||
const onFinish = async (values: any) => {
|
||||
console.info(values);
|
||||
const fileList = values.avatar;
|
||||
let avatar;
|
||||
|
||||
if (Array.isArray(fileList)) {
|
||||
avatar = fileList[0].thumbUrl;
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: 'kSModel/updateKb',
|
||||
payload: {
|
||||
...values,
|
||||
avatar,
|
||||
kb_id: knowledgeBaseId,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const onFinishFailed = (errorInfo: any) => {
|
||||
console.log('Failed:', errorInfo);
|
||||
};
|
||||
|
||||
const fetchLlmList = useCallback(() => {
|
||||
dispatch({
|
||||
type: 'settingModel/llm_list',
|
||||
payload: { model_type: 'embedding' },
|
||||
});
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
const avatar = knowledgeDetails.avatar;
|
||||
let fileList: UploadFile[] = [];
|
||||
|
||||
if (avatar) {
|
||||
fileList = [{ uid: '1', name: 'file', thumbUrl: avatar, status: 'done' }];
|
||||
}
|
||||
form.setFieldsValue({
|
||||
...pick(knowledgeDetails, [
|
||||
'description',
|
||||
'name',
|
||||
'permission',
|
||||
'embd_id',
|
||||
'parser_id',
|
||||
]),
|
||||
avatar: fileList,
|
||||
});
|
||||
}, [form, knowledgeDetails]);
|
||||
|
||||
useFetchParserList();
|
||||
useFetchKnowledgeBaseConfiguration();
|
||||
|
||||
useEffect(() => {
|
||||
fetchLlmList();
|
||||
}, [fetchLlmList]);
|
||||
|
||||
return (
|
||||
<div className={styles.configurationWrapper}>
|
||||
<Title level={5}>Configuration</Title>
|
||||
<p>Update your knowledge base details especially parsing method here.</p>
|
||||
<Divider></Divider>
|
||||
<Form
|
||||
form={form}
|
||||
name="validateOnly"
|
||||
layout="vertical"
|
||||
autoComplete="off"
|
||||
onFinish={onFinish}
|
||||
onFinishFailed={onFinishFailed}
|
||||
>
|
||||
<Form.Item
|
||||
name="name"
|
||||
label="Knowledge base name"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="avatar"
|
||||
label="Knowledge base photo"
|
||||
valuePropName="fileList"
|
||||
getValueFromEvent={normFile}
|
||||
>
|
||||
<Upload
|
||||
listType="picture-card"
|
||||
maxCount={1}
|
||||
showUploadList={{ showPreviewIcon: false, showRemoveIcon: false }}
|
||||
>
|
||||
<button style={{ border: 0, background: 'none' }} type="button">
|
||||
<PlusOutlined />
|
||||
<div style={{ marginTop: 8 }}>Upload</div>
|
||||
</button>
|
||||
</Upload>
|
||||
</Form.Item>
|
||||
<Form.Item name="description" label="Knowledge base bio">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="permission"
|
||||
label="Permissions"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Radio.Group>
|
||||
<Radio value="me">Only me</Radio>
|
||||
<Radio value="team">Team</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="embd_id"
|
||||
label="Embedding Model"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="Please select a country"
|
||||
options={embeddingModelOptions}
|
||||
></Select>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="parser_id"
|
||||
label="Knowledge base category"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Select placeholder="Please select a country">
|
||||
{parserList.map((x) => (
|
||||
<Option value={x.value} key={x.value}>
|
||||
{x.label}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<div className={styles.buttonWrapper}>
|
||||
<Space>
|
||||
<Button htmlType="reset" size={'middle'}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
htmlType="submit"
|
||||
type="primary"
|
||||
size={'middle'}
|
||||
loading={loading}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Configuration;
|
@ -12,7 +12,6 @@
|
||||
|
||||
.left {
|
||||
flex: 1;
|
||||
|
||||
}
|
||||
|
||||
.right {
|
||||
@ -22,3 +21,10 @@
|
||||
padding: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.configurationWrapper {
|
||||
padding: 0 52px;
|
||||
.buttonWrapper {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ 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 styles from './index.less';
|
||||
|
||||
const { CheckableTag } = Tag;
|
||||
@ -12,7 +14,6 @@ const layout = {
|
||||
labelAlign: 'left' as const,
|
||||
};
|
||||
const { Option } = Select;
|
||||
/* eslint-disable no-template-curly-in-string */
|
||||
|
||||
const KnowledgeSetting = () => {
|
||||
const dispatch = useDispatch();
|
||||
@ -44,7 +45,7 @@ const KnowledgeSetting = () => {
|
||||
setSelectedTag(data.data.parser_id);
|
||||
}
|
||||
}
|
||||
}, [knowledgeBaseId]);
|
||||
}, [knowledgeBaseId, dispatch, form]);
|
||||
|
||||
const onFinish = async () => {
|
||||
try {
|
||||
@ -68,11 +69,12 @@ const KnowledgeSetting = () => {
|
||||
parser_id: selectedTag,
|
||||
},
|
||||
});
|
||||
retcode === 0 &&
|
||||
if (retcode === 0) {
|
||||
navigate(
|
||||
`/knowledge/${KnowledgeRouteKey.Dataset}?id=${knowledgeBaseId}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
}
|
||||
@ -158,4 +160,6 @@ const KnowledgeSetting = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default KnowledgeSetting;
|
||||
// export default KnowledgeSetting;
|
||||
|
||||
export default Configuration;
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { IKnowledge } from '@/interfaces/database/knowledge';
|
||||
import kbService from '@/services/kbService';
|
||||
import { message } from 'antd';
|
||||
import { DvaModel } from 'umi';
|
||||
@ -6,6 +7,7 @@ export interface KSModelState {
|
||||
isShowPSwModal: boolean;
|
||||
isShowTntModal: boolean;
|
||||
tenantIfo: any;
|
||||
knowledgeDetails: IKnowledge;
|
||||
}
|
||||
|
||||
const model: DvaModel<KSModelState> = {
|
||||
@ -14,6 +16,7 @@ const model: DvaModel<KSModelState> = {
|
||||
isShowPSwModal: false,
|
||||
isShowTntModal: false,
|
||||
tenantIfo: {},
|
||||
knowledgeDetails: {} as any,
|
||||
},
|
||||
reducers: {
|
||||
updateState(state, { payload }) {
|
||||
@ -22,31 +25,32 @@ const model: DvaModel<KSModelState> = {
|
||||
...payload,
|
||||
};
|
||||
},
|
||||
},
|
||||
subscriptions: {
|
||||
setup({ dispatch, history }) {
|
||||
history.listen((location) => {});
|
||||
setKnowledgeDetails(state, { payload }) {
|
||||
return { ...state, knowledgeDetails: payload };
|
||||
},
|
||||
},
|
||||
effects: {
|
||||
*createKb({ payload = {} }, { call, put }) {
|
||||
*createKb({ payload = {} }, { call }) {
|
||||
const { data } = yield call(kbService.createKb, payload);
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
message.success('创建知识库成功!');
|
||||
message.success('Created successfully!');
|
||||
}
|
||||
return data;
|
||||
},
|
||||
*updateKb({ payload = {} }, { call, put }) {
|
||||
const { data } = yield call(kbService.updateKb, payload);
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
const { retcode } = data;
|
||||
if (retcode === 0) {
|
||||
message.success('更新知识库成功!');
|
||||
yield put({ type: 'getKbDetail', payload: { kb_id: payload.kb_id } });
|
||||
message.success('Updated successfully!');
|
||||
}
|
||||
},
|
||||
*getKbDetail({ payload = {} }, { call, put }) {
|
||||
const { data } = yield call(kbService.get_kb_detail, payload);
|
||||
|
||||
if (data.retcode === 0) {
|
||||
yield put({ type: 'setKnowledgeDetails', payload: data.data });
|
||||
}
|
||||
return data;
|
||||
},
|
||||
},
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { ReactComponent as ConfigrationIcon } from '@/assets/svg/knowledge-configration.svg';
|
||||
import { ReactComponent as ConfigurationIcon } from '@/assets/svg/knowledge-configration.svg';
|
||||
import { ReactComponent as DatasetIcon } from '@/assets/svg/knowledge-dataset.svg';
|
||||
import { ReactComponent as TestingIcon } from '@/assets/svg/knowledge-testing.svg';
|
||||
import { useFetchKnowledgeBaseConfiguration } from '@/hooks/knowledgeHook';
|
||||
import { useSecondPathName } from '@/hooks/routeHook';
|
||||
import { IKnowledge } from '@/interfaces/database/knowledge';
|
||||
import { getWidth } from '@/utils';
|
||||
import { AntDesignOutlined } from '@ant-design/icons';
|
||||
import { Avatar, Menu, MenuProps, Space } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
@ -16,6 +17,9 @@ const KnowledgeSidebar = () => {
|
||||
const { id } = kAModel;
|
||||
let navigate = useNavigate();
|
||||
const activeKey = useSecondPathName();
|
||||
const knowledgeDetails: IKnowledge = useSelector(
|
||||
(state: any) => state.kSModel.knowledgeDetails,
|
||||
);
|
||||
|
||||
const [windowWidth, setWindowWidth] = useState(getWidth());
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
@ -62,12 +66,7 @@ const KnowledgeSidebar = () => {
|
||||
getItem(
|
||||
routeMap[KnowledgeRouteKey.Configuration],
|
||||
KnowledgeRouteKey.Configuration,
|
||||
<ConfigrationIcon />,
|
||||
),
|
||||
getItem(
|
||||
routeMap[KnowledgeRouteKey.TempTesting],
|
||||
KnowledgeRouteKey.TempTesting,
|
||||
<TestingIcon />,
|
||||
<ConfigurationIcon />,
|
||||
),
|
||||
];
|
||||
}, [getItem]);
|
||||
@ -93,16 +92,17 @@ const KnowledgeSidebar = () => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
useFetchKnowledgeBaseConfiguration();
|
||||
|
||||
return (
|
||||
<div className={styles.sidebarWrapper}>
|
||||
<div className={styles.sidebarTop}>
|
||||
<Space size={8} direction="vertical">
|
||||
<Avatar size={64} icon={<AntDesignOutlined />} />
|
||||
<div className={styles.knowledgeTitle}>Cloud Computing</div>
|
||||
<Avatar size={64} src={knowledgeDetails.avatar} />
|
||||
<div className={styles.knowledgeTitle}>{knowledgeDetails.name}</div>
|
||||
</Space>
|
||||
<p className={styles.knowledgeDescription}>
|
||||
A scalable, secure cloud-based database optimized for high-performance
|
||||
computing and data storage.
|
||||
{knowledgeDetails.description}
|
||||
</p>
|
||||
</div>
|
||||
<div className={styles.divider}></div>
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { ITenantInfo } from '@/interfaces/database/knowledge';
|
||||
import { IThirdOAIModelCollection as IThirdAiModelCollection } from '@/interfaces/database/llm';
|
||||
import userService from '@/services/userService';
|
||||
import authorizationUtil from '@/utils/authorizationUtil';
|
||||
import { message } from 'antd';
|
||||
@ -12,7 +13,7 @@ export interface SettingModelState {
|
||||
isShowSSModal: boolean;
|
||||
llm_factory: string;
|
||||
tenantIfo: Nullable<ITenantInfo>;
|
||||
llmInfo: any;
|
||||
llmInfo: IThirdAiModelCollection;
|
||||
myLlm: any[];
|
||||
factoriesList: any[];
|
||||
}
|
||||
@ -126,8 +127,8 @@ const model: DvaModel<SettingModelState> = {
|
||||
}
|
||||
},
|
||||
*llm_list({ payload = {} }, { call, put }) {
|
||||
const { data, response } = yield call(userService.llm_list, payload);
|
||||
const { retcode, data: res, retmsg } = data;
|
||||
const { data } = yield call(userService.llm_list, payload);
|
||||
const { retcode, data: res } = data;
|
||||
if (retcode === 0) {
|
||||
yield put({
|
||||
type: 'updateState',
|
||||
|
@ -44,10 +44,6 @@ const routes = [
|
||||
},
|
||||
{
|
||||
path: '/knowledge/testing',
|
||||
component: '@/pages/add-knowledge/components/knowledge-search',
|
||||
},
|
||||
{
|
||||
path: '/knowledge/tempTesting',
|
||||
component: '@/pages/add-knowledge/components/knowledge-testing',
|
||||
},
|
||||
],
|
||||
|
28
web/src/utils/fileUtil.ts
Normal file
28
web/src/utils/fileUtil.ts
Normal file
@ -0,0 +1,28 @@
|
||||
export const transformFile2Base64 = (val: any): Promise<any> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(val);
|
||||
reader.onload = (): void => {
|
||||
resolve(reader.result);
|
||||
};
|
||||
reader.onerror = reject;
|
||||
});
|
||||
};
|
||||
|
||||
export const transformBase64ToFile = (
|
||||
dataUrl: string,
|
||||
filename: string = 'file',
|
||||
) => {
|
||||
let arr = dataUrl.split(','),
|
||||
bstr = atob(arr[1]),
|
||||
n = bstr.length,
|
||||
u8arr = new Uint8Array(n);
|
||||
|
||||
const mime = arr[0].match(/:(.*?);/);
|
||||
const mimeType = mime ? mime[1] : 'image/png';
|
||||
|
||||
while (n--) {
|
||||
u8arr[n] = bstr.charCodeAt(n);
|
||||
}
|
||||
return new File([u8arr], filename, { type: mimeType });
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user