feat: add pages to ChunkMethodModal (#143)
|
Before Width: | Height: | Size: 298 KiB After Width: | Height: | Size: 757 KiB |
|
Before Width: | Height: | Size: 660 KiB After Width: | Height: | Size: 545 KiB |
|
Before Width: | Height: | Size: 638 KiB |
|
Before Width: | Height: | Size: 902 KiB |
|
Before Width: | Height: | Size: 590 KiB After Width: | Height: | Size: 515 KiB |
|
Before Width: | Height: | Size: 342 KiB After Width: | Height: | Size: 390 KiB |
|
Before Width: | Height: | Size: 461 KiB After Width: | Height: | Size: 168 KiB |
|
Before Width: | Height: | Size: 408 KiB After Width: | Height: | Size: 321 KiB |
79
web/src/assets/svg/chunk-method/one-01.svg
Normal file
|
After Width: | Height: | Size: 590 KiB |
79
web/src/assets/svg/chunk-method/one-02.svg
Normal file
|
After Width: | Height: | Size: 342 KiB |
80
web/src/assets/svg/chunk-method/one-03.svg
Normal file
|
After Width: | Height: | Size: 461 KiB |
79
web/src/assets/svg/chunk-method/one-04.svg
Normal file
|
After Width: | Height: | Size: 408 KiB |
|
Before Width: | Height: | Size: 444 KiB After Width: | Height: | Size: 393 KiB |
|
Before Width: | Height: | Size: 417 KiB After Width: | Height: | Size: 152 KiB |
32
web/src/components/max-token-number.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { Flex, Form, InputNumber, Slider } from 'antd';
|
||||||
|
|
||||||
|
const MaxTokenNumber = () => {
|
||||||
|
return (
|
||||||
|
<Form.Item
|
||||||
|
label="Token number"
|
||||||
|
tooltip="It determine the token number of a chunk approximately."
|
||||||
|
>
|
||||||
|
<Flex gap={20} align="center">
|
||||||
|
<Flex flex={1}>
|
||||||
|
<Form.Item
|
||||||
|
name={['parser_config', 'chunk_token_num']}
|
||||||
|
noStyle
|
||||||
|
initialValue={128}
|
||||||
|
rules={[{ required: true, message: 'Province is required' }]}
|
||||||
|
>
|
||||||
|
<Slider max={2048} style={{ width: '100%' }} />
|
||||||
|
</Form.Item>
|
||||||
|
</Flex>
|
||||||
|
<Form.Item
|
||||||
|
name={['parser_config', 'chunk_token_num']}
|
||||||
|
noStyle
|
||||||
|
rules={[{ required: true, message: 'Street is required' }]}
|
||||||
|
>
|
||||||
|
<InputNumber max={2048} min={0} />
|
||||||
|
</Form.Item>
|
||||||
|
</Flex>
|
||||||
|
</Form.Item>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MaxTokenNumber;
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import { IChunk, IKnowledgeFile } from '@/interfaces/database/knowledge';
|
import { IChunk, IKnowledgeFile } from '@/interfaces/database/knowledge';
|
||||||
|
import { IChangeParserConfigRequestBody } from '@/interfaces/request/document';
|
||||||
import { api_host } from '@/utils/api';
|
import { api_host } from '@/utils/api';
|
||||||
import { buildChunkHighlights } from '@/utils/documentUtils';
|
import { buildChunkHighlights } from '@/utils/documentUtils';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
@ -117,7 +118,11 @@ export const useSetDocumentParser = () => {
|
|||||||
const { knowledgeId } = useGetKnowledgeSearchParams();
|
const { knowledgeId } = useGetKnowledgeSearchParams();
|
||||||
|
|
||||||
const setDocumentParser = useCallback(
|
const setDocumentParser = useCallback(
|
||||||
(parserId: string, documentId: string) => {
|
(
|
||||||
|
parserId: string,
|
||||||
|
documentId: string,
|
||||||
|
parserConfig: IChangeParserConfigRequestBody,
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
return dispatch<any>({
|
return dispatch<any>({
|
||||||
type: 'kFModel/document_change_parser',
|
type: 'kFModel/document_change_parser',
|
||||||
@ -125,6 +130,7 @@ export const useSetDocumentParser = () => {
|
|||||||
parser_id: parserId,
|
parser_id: parserId,
|
||||||
doc_id: documentId,
|
doc_id: documentId,
|
||||||
kb_id: knowledgeId,
|
kb_id: knowledgeId,
|
||||||
|
parser_config: parserConfig,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (errorInfo) {
|
} catch (errorInfo) {
|
||||||
|
|||||||
@ -28,6 +28,12 @@ export interface Parserconfig {
|
|||||||
to_page: number;
|
to_page: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IKnowledgeFileParserConfig {
|
||||||
|
chunk_token_num: number;
|
||||||
|
layout_recognize: boolean;
|
||||||
|
pages: number[][];
|
||||||
|
task_page_size: number;
|
||||||
|
}
|
||||||
export interface IKnowledgeFile {
|
export interface IKnowledgeFile {
|
||||||
chunk_num: number;
|
chunk_num: number;
|
||||||
create_date: string;
|
create_date: string;
|
||||||
@ -51,6 +57,7 @@ export interface IKnowledgeFile {
|
|||||||
type: string;
|
type: string;
|
||||||
update_date: string;
|
update_date: string;
|
||||||
update_time: number;
|
update_time: number;
|
||||||
|
parser_config: IKnowledgeFileParserConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ITenantInfo {
|
export interface ITenantInfo {
|
||||||
|
|||||||
12
web/src/interfaces/request/document.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
export interface IChangeParserConfigRequestBody {
|
||||||
|
pages: number[][];
|
||||||
|
chunk_token_num: number;
|
||||||
|
layout_recognize: boolean;
|
||||||
|
task_page_size: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IChangeParserRequestBody {
|
||||||
|
parser_id: string;
|
||||||
|
doc_id: string;
|
||||||
|
parser_config: IChangeParserConfigRequestBody;
|
||||||
|
}
|
||||||
@ -1,10 +1,23 @@
|
|||||||
import { IModalManagerChildrenProps } from '@/components/modal-manager';
|
import { IModalManagerChildrenProps } from '@/components/modal-manager';
|
||||||
import {
|
import {
|
||||||
useFetchTenantInfo,
|
Button,
|
||||||
useSelectParserList,
|
Divider,
|
||||||
} from '@/hooks/userSettingHook';
|
Form,
|
||||||
import { Modal, Space, Tag } from 'antd';
|
InputNumber,
|
||||||
import React, { useEffect, useState } from 'react';
|
Modal,
|
||||||
|
Space,
|
||||||
|
Switch,
|
||||||
|
Tag,
|
||||||
|
} from 'antd';
|
||||||
|
import React, { useEffect, useMemo } from 'react';
|
||||||
|
|
||||||
|
import MaxTokenNumber from '@/components/max-token-number';
|
||||||
|
import { IKnowledgeFileParserConfig } from '@/interfaces/database/knowledge';
|
||||||
|
import { IChangeParserConfigRequestBody } from '@/interfaces/request/document';
|
||||||
|
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
||||||
|
import omit from 'lodash/omit';
|
||||||
|
import {} from 'module';
|
||||||
|
import { useFetchParserListOnMount } from './hooks';
|
||||||
|
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
@ -12,41 +25,74 @@ const { CheckableTag } = Tag;
|
|||||||
|
|
||||||
interface IProps extends Omit<IModalManagerChildrenProps, 'showModal'> {
|
interface IProps extends Omit<IModalManagerChildrenProps, 'showModal'> {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
onOk: (parserId: string) => void;
|
onOk: (
|
||||||
|
parserId: string,
|
||||||
|
parserConfig: IChangeParserConfigRequestBody,
|
||||||
|
) => void;
|
||||||
showModal?(): void;
|
showModal?(): void;
|
||||||
parser_id: string;
|
parserId: string;
|
||||||
|
parserConfig: IKnowledgeFileParserConfig;
|
||||||
|
documentType: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hidePagesChunkMethods = ['qa', 'table', 'picture', 'resume', 'one'];
|
||||||
|
|
||||||
const ChunkMethodModal: React.FC<IProps> = ({
|
const ChunkMethodModal: React.FC<IProps> = ({
|
||||||
parser_id,
|
parserId,
|
||||||
onOk,
|
onOk,
|
||||||
hideModal,
|
hideModal,
|
||||||
visible,
|
visible,
|
||||||
|
documentType,
|
||||||
|
parserConfig,
|
||||||
}) => {
|
}) => {
|
||||||
const [selectedTag, setSelectedTag] = useState('');
|
const { parserList, handleChange, selectedTag } =
|
||||||
const parserList = useSelectParserList();
|
useFetchParserListOnMount(parserId);
|
||||||
|
const [form] = Form.useForm();
|
||||||
useFetchTenantInfo();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setSelectedTag(parser_id);
|
|
||||||
}, [parser_id]);
|
|
||||||
|
|
||||||
const handleOk = async () => {
|
const handleOk = async () => {
|
||||||
onOk(selectedTag);
|
const values = await form.validateFields();
|
||||||
|
console.info(values);
|
||||||
|
const parser_config = {
|
||||||
|
...values.parser_config,
|
||||||
|
pages: values.pages?.map((x: any) => [x.from, x.to]) ?? [],
|
||||||
|
};
|
||||||
|
console.info(parser_config);
|
||||||
|
onOk(selectedTag, parser_config);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = (tag: string, checked: boolean) => {
|
const showPages = useMemo(() => {
|
||||||
const nextSelectedTag = checked ? tag : selectedTag;
|
return (
|
||||||
setSelectedTag(nextSelectedTag);
|
documentType === 'pdf' &&
|
||||||
|
hidePagesChunkMethods.every((x) => x !== selectedTag)
|
||||||
|
);
|
||||||
|
}, [documentType, selectedTag]);
|
||||||
|
|
||||||
|
const showOne = useMemo(() => {
|
||||||
|
return showPages || selectedTag === 'one';
|
||||||
|
}, [showPages, selectedTag]);
|
||||||
|
|
||||||
|
const afterClose = () => {
|
||||||
|
form.resetFields();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (visible) {
|
||||||
|
const pages =
|
||||||
|
parserConfig.pages?.map((x) => ({ from: x[0], to: x[1] })) ?? [];
|
||||||
|
form.setFieldsValue({
|
||||||
|
pages: pages.length > 0 ? pages : [{ from: 1, to: 1024 }],
|
||||||
|
parser_config: omit(parserConfig, 'pages'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [form, parserConfig, visible]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title="Chunk Method"
|
title="Chunk Method"
|
||||||
open={visible}
|
open={visible}
|
||||||
onOk={handleOk}
|
onOk={handleOk}
|
||||||
onCancel={hideModal}
|
onCancel={hideModal}
|
||||||
|
afterClose={afterClose}
|
||||||
>
|
>
|
||||||
<Space size={[0, 8]} wrap>
|
<Space size={[0, 8]} wrap>
|
||||||
<div className={styles.tags}>
|
<div className={styles.tags}>
|
||||||
@ -63,6 +109,138 @@ const ChunkMethodModal: React.FC<IProps> = ({
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</Space>
|
</Space>
|
||||||
|
<Divider></Divider>
|
||||||
|
{
|
||||||
|
<Form name="dynamic_form_nest_item" autoComplete="off" form={form}>
|
||||||
|
{showPages && (
|
||||||
|
<>
|
||||||
|
<Form.List name="pages">
|
||||||
|
{(fields, { add, remove }) => (
|
||||||
|
<>
|
||||||
|
{fields.map(({ key, name, ...restField }) => (
|
||||||
|
<Space
|
||||||
|
key={key}
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
}}
|
||||||
|
align="baseline"
|
||||||
|
>
|
||||||
|
<Form.Item
|
||||||
|
{...restField}
|
||||||
|
name={[name, 'from']}
|
||||||
|
dependencies={name > 0 ? [name - 1, 'to'] : []}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: 'Missing start page number',
|
||||||
|
},
|
||||||
|
({ getFieldValue }) => ({
|
||||||
|
validator(_, value) {
|
||||||
|
if (
|
||||||
|
name === 0 ||
|
||||||
|
!value ||
|
||||||
|
getFieldValue(['pages', name - 1, 'to']) <
|
||||||
|
value
|
||||||
|
) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
return Promise.reject(
|
||||||
|
new Error(
|
||||||
|
'The current value must be greater than the previous to!',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<InputNumber
|
||||||
|
placeholder="from"
|
||||||
|
min={0}
|
||||||
|
precision={0}
|
||||||
|
className={styles.pageInputNumber}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
{...restField}
|
||||||
|
name={[name, 'to']}
|
||||||
|
dependencies={[name, 'from']}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: 'Missing end page number(excluding)',
|
||||||
|
},
|
||||||
|
({ getFieldValue }) => ({
|
||||||
|
validator(_, value) {
|
||||||
|
if (
|
||||||
|
!value ||
|
||||||
|
getFieldValue(['pages', name, 'from']) < value
|
||||||
|
) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
return Promise.reject(
|
||||||
|
new Error(
|
||||||
|
'The current value must be greater than to!',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<InputNumber
|
||||||
|
placeholder="to"
|
||||||
|
min={0}
|
||||||
|
precision={0}
|
||||||
|
className={styles.pageInputNumber}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
{name > 0 && (
|
||||||
|
<MinusCircleOutlined onClick={() => remove(name)} />
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
))}
|
||||||
|
<Form.Item>
|
||||||
|
<Button
|
||||||
|
type="dashed"
|
||||||
|
onClick={() => add()}
|
||||||
|
block
|
||||||
|
icon={<PlusOutlined />}
|
||||||
|
>
|
||||||
|
Add page
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Form.List>
|
||||||
|
<Form.Item
|
||||||
|
name={['parser_config', 'task_page_size']}
|
||||||
|
label="Task page size"
|
||||||
|
tooltip={'coming soon'}
|
||||||
|
initialValue={2}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: 'Please input your task page size!',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<InputNumber min={1} max={128} />
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{showOne && (
|
||||||
|
<Form.Item
|
||||||
|
name={['parser_config', 'layout_recognize']}
|
||||||
|
label="Layout recognize"
|
||||||
|
initialValue={true}
|
||||||
|
valuePropName="checked"
|
||||||
|
tooltip={'coming soon'}
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
{selectedTag === 'naive' && <MaxTokenNumber></MaxTokenNumber>}
|
||||||
|
</Form>
|
||||||
|
}
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,9 +7,13 @@ import {
|
|||||||
} from '@/hooks/documentHooks';
|
} from '@/hooks/documentHooks';
|
||||||
import { useGetKnowledgeSearchParams } from '@/hooks/routeHook';
|
import { useGetKnowledgeSearchParams } from '@/hooks/routeHook';
|
||||||
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
||||||
import { useFetchTenantInfo } from '@/hooks/userSettingHook';
|
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 { IChangeParserConfigRequestBody } from '@/interfaces/request/document';
|
||||||
import { PaginationProps } from 'antd';
|
import { PaginationProps } from 'antd';
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { useDispatch, useNavigate, useSelector } from 'umi';
|
import { useDispatch, useNavigate, useSelector } from 'umi';
|
||||||
@ -222,8 +226,8 @@ export const useChangeDocumentParser = (documentId: string) => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const onChangeParserOk = useCallback(
|
const onChangeParserOk = useCallback(
|
||||||
async (parserId: string) => {
|
async (parserId: string, parserConfig: IChangeParserConfigRequestBody) => {
|
||||||
const ret = await setDocumentParser(parserId, documentId);
|
const ret = await setDocumentParser(parserId, documentId, parserConfig);
|
||||||
if (ret === 0) {
|
if (ret === 0) {
|
||||||
hideChangeParserModal();
|
hideChangeParserModal();
|
||||||
}
|
}
|
||||||
@ -239,3 +243,21 @@ export const useChangeDocumentParser = (documentId: string) => {
|
|||||||
showChangeParserModal,
|
showChangeParserModal,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useFetchParserListOnMount = (parserId: string) => {
|
||||||
|
const [selectedTag, setSelectedTag] = useState('');
|
||||||
|
const parserList = useSelectParserList();
|
||||||
|
|
||||||
|
useFetchTenantInfo();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSelectedTag(parserId);
|
||||||
|
}, [parserId]);
|
||||||
|
|
||||||
|
const handleChange = (tag: string, checked: boolean) => {
|
||||||
|
const nextSelectedTag = checked ? tag : selectedTag;
|
||||||
|
setSelectedTag(nextSelectedTag);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { parserList, handleChange, selectedTag };
|
||||||
|
};
|
||||||
|
|||||||
@ -34,3 +34,7 @@
|
|||||||
.tochunks {
|
.tochunks {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pageInputNumber {
|
||||||
|
width: 220px;
|
||||||
|
}
|
||||||
|
|||||||
@ -224,7 +224,9 @@ const KnowledgeFile = () => {
|
|||||||
onOk={onCreateOk}
|
onOk={onCreateOk}
|
||||||
/>
|
/>
|
||||||
<ChunkMethodModal
|
<ChunkMethodModal
|
||||||
parser_id={currentRecord.parser_id}
|
parserId={currentRecord.parser_id}
|
||||||
|
parserConfig={currentRecord.parser_config}
|
||||||
|
documentType={currentRecord.type}
|
||||||
onOk={onChangeParserOk}
|
onOk={onChangeParserOk}
|
||||||
visible={changeParserVisible}
|
visible={changeParserVisible}
|
||||||
hideModal={hideChangeParserModal}
|
hideModal={hideChangeParserModal}
|
||||||
|
|||||||
@ -7,10 +7,6 @@ import pick from 'lodash/pick';
|
|||||||
import { DvaModel } from 'umi';
|
import { DvaModel } from 'umi';
|
||||||
|
|
||||||
export interface KFModelState extends BaseState {
|
export interface KFModelState extends BaseState {
|
||||||
isShowCEFwModal: boolean;
|
|
||||||
isShowTntModal: boolean;
|
|
||||||
isShowSegmentSetModal: boolean;
|
|
||||||
isShowRenameModal: boolean;
|
|
||||||
tenantIfo: any;
|
tenantIfo: any;
|
||||||
data: IKnowledgeFile[];
|
data: IKnowledgeFile[];
|
||||||
total: number;
|
total: number;
|
||||||
@ -21,10 +17,6 @@ export interface KFModelState extends BaseState {
|
|||||||
const model: DvaModel<KFModelState> = {
|
const model: DvaModel<KFModelState> = {
|
||||||
namespace: 'kFModel',
|
namespace: 'kFModel',
|
||||||
state: {
|
state: {
|
||||||
isShowCEFwModal: false,
|
|
||||||
isShowTntModal: false,
|
|
||||||
isShowSegmentSetModal: false,
|
|
||||||
isShowRenameModal: false,
|
|
||||||
tenantIfo: {},
|
tenantIfo: {},
|
||||||
data: [],
|
data: [],
|
||||||
total: 0,
|
total: 0,
|
||||||
@ -43,9 +35,7 @@ const model: DvaModel<KFModelState> = {
|
|||||||
...payload,
|
...payload,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
setIsShowRenameModal(state, { payload }) {
|
|
||||||
return { ...state, isShowRenameModal: payload };
|
|
||||||
},
|
|
||||||
setCurrentRecord(state, { payload }) {
|
setCurrentRecord(state, { payload }) {
|
||||||
return { ...state, currentRecord: payload };
|
return { ...state, currentRecord: payload };
|
||||||
},
|
},
|
||||||
@ -120,7 +110,7 @@ const model: DvaModel<KFModelState> = {
|
|||||||
const { retcode } = data;
|
const { retcode } = data;
|
||||||
if (retcode === 0) {
|
if (retcode === 0) {
|
||||||
message.success('Modified!');
|
message.success('Modified!');
|
||||||
put({
|
yield put({
|
||||||
type: 'getKfList',
|
type: 'getKfList',
|
||||||
payload: { kb_id: payload.kb_id },
|
payload: { kb_id: payload.kb_id },
|
||||||
});
|
});
|
||||||
@ -148,10 +138,7 @@ const model: DvaModel<KFModelState> = {
|
|||||||
const { retcode } = data;
|
const { retcode } = data;
|
||||||
if (retcode === 0) {
|
if (retcode === 0) {
|
||||||
message.success('rename success!');
|
message.success('rename success!');
|
||||||
yield put({
|
|
||||||
type: 'setIsShowRenameModal',
|
|
||||||
payload: false,
|
|
||||||
});
|
|
||||||
yield put({
|
yield put({
|
||||||
type: 'getKfList',
|
type: 'getKfList',
|
||||||
payload: { kb_id: payload.kb_id },
|
payload: { kb_id: payload.kb_id },
|
||||||
@ -164,16 +151,11 @@ const model: DvaModel<KFModelState> = {
|
|||||||
const { data } = yield call(kbService.document_create, payload);
|
const { data } = yield call(kbService.document_create, payload);
|
||||||
const { retcode } = data;
|
const { retcode } = data;
|
||||||
if (retcode === 0) {
|
if (retcode === 0) {
|
||||||
put({
|
yield put({
|
||||||
type: 'getKfList',
|
type: 'getKfList',
|
||||||
payload: { kb_id: payload.kb_id },
|
payload: { kb_id: payload.kb_id },
|
||||||
});
|
});
|
||||||
put({
|
|
||||||
type: 'kFModel/updateState',
|
|
||||||
payload: {
|
|
||||||
isShowCEFwModal: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
message.success('Created!');
|
message.success('Created!');
|
||||||
}
|
}
|
||||||
return retcode;
|
return retcode;
|
||||||
@ -202,16 +184,11 @@ const model: DvaModel<KFModelState> = {
|
|||||||
);
|
);
|
||||||
const { retcode } = data;
|
const { retcode } = data;
|
||||||
if (retcode === 0) {
|
if (retcode === 0) {
|
||||||
put({
|
yield put({
|
||||||
type: 'getKfList',
|
type: 'getKfList',
|
||||||
payload: { kb_id: payload.kb_id },
|
payload: { kb_id: payload.kb_id },
|
||||||
});
|
});
|
||||||
put({
|
|
||||||
type: 'updateState',
|
|
||||||
payload: {
|
|
||||||
isShowSegmentSetModal: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
message.success('Modified!');
|
message.success('Modified!');
|
||||||
}
|
}
|
||||||
return retcode;
|
return retcode;
|
||||||
|
|||||||
@ -1,22 +1,12 @@
|
|||||||
import { normFile } from '@/utils/fileUtil';
|
import { normFile } from '@/utils/fileUtil';
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
import {
|
import { Button, Form, Input, Radio, Select, Space, Upload } from 'antd';
|
||||||
Button,
|
|
||||||
Flex,
|
|
||||||
Form,
|
|
||||||
Input,
|
|
||||||
InputNumber,
|
|
||||||
Radio,
|
|
||||||
Select,
|
|
||||||
Slider,
|
|
||||||
Space,
|
|
||||||
Upload,
|
|
||||||
} from 'antd';
|
|
||||||
import {
|
import {
|
||||||
useFetchKnowledgeConfigurationOnMount,
|
useFetchKnowledgeConfigurationOnMount,
|
||||||
useSubmitKnowledgeConfiguration,
|
useSubmitKnowledgeConfiguration,
|
||||||
} from './hooks';
|
} from './hooks';
|
||||||
|
|
||||||
|
import MaxTokenNumber from '@/components/max-token-number';
|
||||||
import { FormInstance } from 'antd/lib';
|
import { FormInstance } from 'antd/lib';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
@ -121,35 +111,7 @@ const ConfigurationForm = ({ form }: { form: FormInstance }) => {
|
|||||||
const parserId = getFieldValue('parser_id');
|
const parserId = getFieldValue('parser_id');
|
||||||
|
|
||||||
if (parserId === 'naive') {
|
if (parserId === 'naive') {
|
||||||
return (
|
return <MaxTokenNumber></MaxTokenNumber>;
|
||||||
<Form.Item label="Token number" tooltip="It determine the token number of a chunk approximately.">
|
|
||||||
<Flex gap={20} align="center">
|
|
||||||
<Flex flex={1}>
|
|
||||||
<Form.Item
|
|
||||||
name={['parser_config', 'chunk_token_num']}
|
|
||||||
noStyle
|
|
||||||
initialValue={128}
|
|
||||||
rules={[
|
|
||||||
{ required: true, message: 'Province is required' },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Slider className={styles.variableSlider} max={2048} />
|
|
||||||
</Form.Item>
|
|
||||||
</Flex>
|
|
||||||
<Form.Item
|
|
||||||
name={['parser_config', 'chunk_token_num']}
|
|
||||||
noStyle
|
|
||||||
rules={[{ required: true, message: 'Street is required' }]}
|
|
||||||
>
|
|
||||||
<InputNumber
|
|
||||||
className={styles.sliderInputNumber}
|
|
||||||
max={2048}
|
|
||||||
min={0}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</Flex>
|
|
||||||
</Form.Item>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import { DvaModel } from 'umi';
|
|||||||
|
|
||||||
export interface KSModelState {
|
export interface KSModelState {
|
||||||
isShowPSwModal: boolean;
|
isShowPSwModal: boolean;
|
||||||
isShowTntModal: boolean;
|
|
||||||
tenantIfo: any;
|
tenantIfo: any;
|
||||||
knowledgeDetails: IKnowledge;
|
knowledgeDetails: IKnowledge;
|
||||||
}
|
}
|
||||||
@ -14,7 +13,6 @@ const model: DvaModel<KSModelState> = {
|
|||||||
namespace: 'kSModel',
|
namespace: 'kSModel',
|
||||||
state: {
|
state: {
|
||||||
isShowPSwModal: false,
|
isShowPSwModal: false,
|
||||||
isShowTntModal: false,
|
|
||||||
tenantIfo: {},
|
tenantIfo: {},
|
||||||
knowledgeDetails: {} as any,
|
knowledgeDetails: {} as any,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -5,9 +5,9 @@ const getImageName = (prefix: string, length: number) =>
|
|||||||
|
|
||||||
export const ImageMap = {
|
export const ImageMap = {
|
||||||
book: getImageName('book', 4),
|
book: getImageName('book', 4),
|
||||||
laws: getImageName('law', 4),
|
laws: getImageName('law', 2),
|
||||||
manual: getImageName('manual', 4),
|
manual: getImageName('manual', 4),
|
||||||
picture: getImageName('picture', 2),
|
picture: getImageName('media', 2),
|
||||||
naive: getImageName('naive', 2),
|
naive: getImageName('naive', 2),
|
||||||
paper: getImageName('paper', 2),
|
paper: getImageName('paper', 2),
|
||||||
presentation: getImageName('presentation', 2),
|
presentation: getImageName('presentation', 2),
|
||||||
@ -32,10 +32,13 @@ export const TextMap = {
|
|||||||
The chunk granularity is consistent with 'ARTICLE', and all the upper level text will be included in the chunk.
|
The chunk granularity is consistent with 'ARTICLE', and all the upper level text will be included in the chunk.
|
||||||
</p>`,
|
</p>`,
|
||||||
},
|
},
|
||||||
manual: { title: '', description: `<p>Only <b>PDF</b> is supported.</p><p>
|
manual: {
|
||||||
|
title: '',
|
||||||
|
description: `<p>Only <b>PDF</b> is supported.</p><p>
|
||||||
We assume manual has hierarchical section structure. We use the lowest section titles as pivots to slice documents.
|
We assume manual has hierarchical section structure. We use the lowest section titles as pivots to slice documents.
|
||||||
So, the figures and tables in the same section will not be sliced apart, and chunk size might be large.
|
So, the figures and tables in the same section will not be sliced apart, and chunk size might be large.
|
||||||
</p>` },
|
</p>`,
|
||||||
|
},
|
||||||
naive: {
|
naive: {
|
||||||
title: '',
|
title: '',
|
||||||
description: `<p>Supported file formats are <b>DOCX, EXCEL, PPT, IMAGE, PDF, TXT</b>.</p>
|
description: `<p>Supported file formats are <b>DOCX, EXCEL, PPT, IMAGE, PDF, TXT</b>.</p>
|
||||||
@ -100,19 +103,19 @@ export const TextMap = {
|
|||||||
</li>
|
</li>
|
||||||
<li>Every row in table will be treated as a chunk.</li>
|
<li>Every row in table will be treated as a chunk.</li>
|
||||||
</ul>`,
|
</ul>`,
|
||||||
},
|
},
|
||||||
picture: {
|
picture: {
|
||||||
title: '',
|
title: '',
|
||||||
description: `
|
description: `
|
||||||
<p>Image files are supported. Video is coming soon.</p><p>
|
<p>Image files are supported. Video is coming soon.</p><p>
|
||||||
If the picture has text in it, OCR is applied to extract the text as its text description.
|
If the picture has text in it, OCR is applied to extract the text as its text description.
|
||||||
</p><p>
|
</p><p>
|
||||||
If the text extracted by OCR is not enough, visual LLM is used to get the descriptions.
|
If the text extracted by OCR is not enough, visual LLM is used to get the descriptions.
|
||||||
</p>`,
|
</p>`,
|
||||||
},
|
},
|
||||||
one: {
|
one: {
|
||||||
title: '',
|
title: '',
|
||||||
description: `
|
description: `
|
||||||
<p>Supported file formats are <b>DOCX, EXCEL, PDF, TXT</b>.
|
<p>Supported file formats are <b>DOCX, EXCEL, PDF, TXT</b>.
|
||||||
</p><p>
|
</p><p>
|
||||||
For a document, it will be treated as an entire chunk, no split at all.
|
For a document, it will be treated as an entire chunk, no split at all.
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { DvaModel } from 'umi';
|
import { DvaModel } from 'umi';
|
||||||
export interface kAModelState {
|
export interface kAModelState {
|
||||||
isShowPSwModal: boolean;
|
isShowPSwModal: boolean;
|
||||||
isShowTntModal: boolean;
|
|
||||||
tenantIfo: any;
|
tenantIfo: any;
|
||||||
id: string;
|
id: string;
|
||||||
doc_id: string;
|
doc_id: string;
|
||||||
@ -11,7 +10,6 @@ const model: DvaModel<kAModelState> = {
|
|||||||
namespace: 'kAModel',
|
namespace: 'kAModel',
|
||||||
state: {
|
state: {
|
||||||
isShowPSwModal: false,
|
isShowPSwModal: false,
|
||||||
isShowTntModal: false,
|
|
||||||
tenantIfo: {},
|
tenantIfo: {},
|
||||||
id: '',
|
id: '',
|
||||||
doc_id: '',
|
doc_id: '',
|
||||||
|
|||||||
@ -1,78 +0,0 @@
|
|||||||
import { rsaPsw } from '@/utils';
|
|
||||||
import { Form, Input, Modal } from 'antd';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { useDispatch, useSelector } from 'umi';
|
|
||||||
|
|
||||||
type FieldType = {
|
|
||||||
newPassword?: string;
|
|
||||||
password?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const CpwModal = () => {
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const settingModel = useSelector((state: any) => state.settingModel);
|
|
||||||
const { isShowPSwModal } = settingModel;
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const [form] = Form.useForm();
|
|
||||||
|
|
||||||
const handleCancel = () => {
|
|
||||||
dispatch({
|
|
||||||
type: 'settingModel/updateState',
|
|
||||||
payload: {
|
|
||||||
isShowPSwModal: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const handleOk = async () => {
|
|
||||||
try {
|
|
||||||
const values = await form.validateFields();
|
|
||||||
var password = rsaPsw(values.password);
|
|
||||||
var new_password = rsaPsw(values.newPassword);
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: 'settingModel/setting',
|
|
||||||
payload: {
|
|
||||||
password,
|
|
||||||
new_password,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} catch (errorInfo) {
|
|
||||||
console.log('Failed:', errorInfo);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
title="Basic Modal"
|
|
||||||
open={isShowPSwModal}
|
|
||||||
onOk={handleOk}
|
|
||||||
onCancel={handleCancel}
|
|
||||||
>
|
|
||||||
<Form
|
|
||||||
form={form}
|
|
||||||
labelCol={{ span: 8 }}
|
|
||||||
wrapperCol={{ span: 16 }}
|
|
||||||
style={{ maxWidth: 600 }}
|
|
||||||
autoComplete="off"
|
|
||||||
>
|
|
||||||
<Form.Item<FieldType>
|
|
||||||
label="旧密码"
|
|
||||||
name="password"
|
|
||||||
rules={[{ required: true, message: 'Please input value' }]}
|
|
||||||
>
|
|
||||||
<Input.Password />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item<FieldType>
|
|
||||||
label="新密码"
|
|
||||||
name="newPassword"
|
|
||||||
rules={[
|
|
||||||
{ required: true, message: 'Please input your newPassword!' },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Input.Password />
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
export default CpwModal;
|
|
||||||
@ -1,146 +0,0 @@
|
|||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
import { RadarChartOutlined } from '@ant-design/icons';
|
|
||||||
import { ProCard } from '@ant-design/pro-components';
|
|
||||||
import { Button, Card, Col, Row, Tag } from 'antd';
|
|
||||||
import { useDispatch, useSelector } from 'umi';
|
|
||||||
|
|
||||||
interface DataType {
|
|
||||||
key: React.Key;
|
|
||||||
name: string;
|
|
||||||
age: number;
|
|
||||||
address: string;
|
|
||||||
description: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SettingList = () => {
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const settingModel = useSelector((state: any) => state.settingModel);
|
|
||||||
const { llmInfo = {}, factoriesList, myLlm = [] } = settingModel;
|
|
||||||
const { OpenAI = [], tongyi = [] } = llmInfo;
|
|
||||||
const [collapsed, setCollapsed] = useState(true);
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
dispatch({
|
|
||||||
type: 'settingModel/factories_list',
|
|
||||||
payload: {},
|
|
||||||
});
|
|
||||||
dispatch({
|
|
||||||
type: 'settingModel/llm_list',
|
|
||||||
payload: {},
|
|
||||||
});
|
|
||||||
dispatch({
|
|
||||||
type: 'settingModel/my_llm',
|
|
||||||
payload: {},
|
|
||||||
});
|
|
||||||
}, [dispatch]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={styles.list}
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
padding: 24,
|
|
||||||
gap: 12,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{myLlm.map((item: any) => {
|
|
||||||
return (
|
|
||||||
<ProCard
|
|
||||||
key={item.llm_factory}
|
|
||||||
// title={<div>可折叠-图标自定义</div>}
|
|
||||||
collapsibleIconRender={({
|
|
||||||
collapsed: buildInCollapsed,
|
|
||||||
}: {
|
|
||||||
collapsed: boolean;
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h3>
|
|
||||||
<RadarChartOutlined />
|
|
||||||
{item.llm_factory}
|
|
||||||
</h3>
|
|
||||||
<div>
|
|
||||||
{item.tags.split(',').map((d: string) => {
|
|
||||||
return <Tag key={d}>{d}</Tag>;
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
{buildInCollapsed ? (
|
|
||||||
<span>显示{OpenAI.length}个模型</span>
|
|
||||||
) : (
|
|
||||||
<span>收起{OpenAI.length}个模型 </span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
extra={
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
type="link"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
dispatch({
|
|
||||||
type: 'settingModel/updateState',
|
|
||||||
payload: {
|
|
||||||
llm_factory: item.llm_factory,
|
|
||||||
isShowSAKModal: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
设置
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
style={{ marginBlockStart: 16 }}
|
|
||||||
headerBordered
|
|
||||||
collapsible
|
|
||||||
defaultCollapsed
|
|
||||||
></ProCard>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
|
|
||||||
<Row gutter={{ xs: 8, sm: 16, md: 24, lg: 32 }}>
|
|
||||||
{factoriesList.map((item: any) => {
|
|
||||||
return (
|
|
||||||
<Col key={item.name} xs={24} sm={12} md={8} lg={6}>
|
|
||||||
<Card
|
|
||||||
title={item.name}
|
|
||||||
bordered={false}
|
|
||||||
extra={
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
type="link"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
dispatch({
|
|
||||||
type: 'settingModel/updateState',
|
|
||||||
payload: {
|
|
||||||
llm_factory: item.name,
|
|
||||||
isShowSAKModal: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
设置
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
{item.tags.split(',').map((d: string) => {
|
|
||||||
return <Tag key={d}>{d}</Tag>;
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
</Col>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Row>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
export default SettingList;
|
|
||||||
@ -1,66 +0,0 @@
|
|||||||
import { Form, Input, Modal } from 'antd';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { useDispatch, useSelector } from 'umi';
|
|
||||||
|
|
||||||
type FieldType = {
|
|
||||||
api_key?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const SakModal = () => {
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const settingModel = useSelector((state: any) => state.settingModel);
|
|
||||||
const { isShowSAKModal, llm_factory } = settingModel;
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const [form] = Form.useForm();
|
|
||||||
|
|
||||||
const handleCancel = () => {
|
|
||||||
dispatch({
|
|
||||||
type: 'settingModel/updateState',
|
|
||||||
payload: {
|
|
||||||
isShowSAKModal: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const handleOk = async () => {
|
|
||||||
try {
|
|
||||||
const values = await form.validateFields();
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: 'settingModel/set_api_key',
|
|
||||||
payload: {
|
|
||||||
api_key: values.api_key,
|
|
||||||
llm_factory: llm_factory,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} catch (errorInfo) {
|
|
||||||
console.log('Failed:', errorInfo);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
title="Basic Modal"
|
|
||||||
open={isShowSAKModal}
|
|
||||||
onOk={handleOk}
|
|
||||||
onCancel={handleCancel}
|
|
||||||
>
|
|
||||||
<Form
|
|
||||||
form={form}
|
|
||||||
name="validateOnly"
|
|
||||||
labelCol={{ span: 8 }}
|
|
||||||
wrapperCol={{ span: 16 }}
|
|
||||||
style={{ maxWidth: 600 }}
|
|
||||||
autoComplete="off"
|
|
||||||
>
|
|
||||||
<Form.Item<FieldType>
|
|
||||||
label="API Key"
|
|
||||||
name="api_key"
|
|
||||||
rules={[{ required: true, message: 'Please input ' }]}
|
|
||||||
>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
export default SakModal;
|
|
||||||
@ -1,144 +0,0 @@
|
|||||||
import { Form, Modal, Select } from 'antd';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { useDispatch, useSelector } from 'umi';
|
|
||||||
|
|
||||||
type FieldType = {
|
|
||||||
embd_id?: string;
|
|
||||||
img2txt_id?: string;
|
|
||||||
llm_id?: string;
|
|
||||||
asr_id?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const SsModal = () => {
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const settingModel = useSelector((state: any) => state.settingModel);
|
|
||||||
const { isShowSSModal, llmInfo = {}, tenantIfo } = settingModel;
|
|
||||||
const [form] = Form.useForm();
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const handleCancel = () => {
|
|
||||||
dispatch({
|
|
||||||
type: 'settingModel/updateState',
|
|
||||||
payload: {
|
|
||||||
isShowSSModal: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOk = async () => {
|
|
||||||
try {
|
|
||||||
const values = await form.validateFields();
|
|
||||||
const retcode = await dispatch<any>({
|
|
||||||
type: 'settingModel/set_tenant_info',
|
|
||||||
payload: {
|
|
||||||
...values,
|
|
||||||
tenant_id: tenantIfo.tenant_id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
retcode === 0 &&
|
|
||||||
dispatch({
|
|
||||||
type: 'settingModel/updateState',
|
|
||||||
payload: {
|
|
||||||
isShowSSModal: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} catch (errorInfo) {
|
|
||||||
console.log('Failed:', errorInfo);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleChange = () => {};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
title="Basic Modal"
|
|
||||||
open={isShowSSModal}
|
|
||||||
onOk={handleOk}
|
|
||||||
onCancel={handleCancel}
|
|
||||||
>
|
|
||||||
<Form
|
|
||||||
form={form}
|
|
||||||
name="validateOnly"
|
|
||||||
// labelCol={{ span: 8 }}
|
|
||||||
// wrapperCol={{ span: 16 }}
|
|
||||||
style={{ maxWidth: 600 }}
|
|
||||||
autoComplete="off"
|
|
||||||
layout="vertical"
|
|
||||||
>
|
|
||||||
<Form.Item<FieldType>
|
|
||||||
label="embedding 模型"
|
|
||||||
name="embd_id"
|
|
||||||
rules={[{ required: true, message: 'Please input value' }]}
|
|
||||||
initialValue={tenantIfo.embd_id}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
// style={{ width: 200 }}
|
|
||||||
onChange={handleChange}
|
|
||||||
// fieldNames={label:}
|
|
||||||
options={Object.keys(llmInfo).map((t) => {
|
|
||||||
const options = llmInfo[t]
|
|
||||||
.filter((d: any) => d.model_type === 'embedding')
|
|
||||||
.map((d: any) => ({ label: d.llm_name, value: d.llm_name }));
|
|
||||||
return { label: t, options };
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item<FieldType>
|
|
||||||
label="chat 模型"
|
|
||||||
name="llm_id"
|
|
||||||
rules={[{ required: true, message: 'Please input value' }]}
|
|
||||||
initialValue={tenantIfo.llm_id}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
// style={{ width: 200 }}
|
|
||||||
onChange={handleChange}
|
|
||||||
// fieldNames={label:}
|
|
||||||
options={Object.keys(llmInfo).map((t) => {
|
|
||||||
const options = llmInfo[t]
|
|
||||||
.filter((d: any) => d.model_type === 'chat')
|
|
||||||
.map((d: any) => ({ label: d.llm_name, value: d.llm_name }));
|
|
||||||
return { label: t, options };
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item<FieldType>
|
|
||||||
label="image2text 模型"
|
|
||||||
name="img2txt_id"
|
|
||||||
rules={[{ required: true, message: 'Please input value' }]}
|
|
||||||
initialValue={tenantIfo.img2txt_id}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
// style={{ width: 200 }}
|
|
||||||
onChange={handleChange}
|
|
||||||
// fieldNames={label:}
|
|
||||||
options={Object.keys(llmInfo).map((t) => {
|
|
||||||
const options = llmInfo[t]
|
|
||||||
.filter((d: any) => d.model_type === 'image2text')
|
|
||||||
.map((d: any) => ({ label: d.llm_name, value: d.llm_name }));
|
|
||||||
return { label: t, options };
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item<FieldType>
|
|
||||||
label="speech2text 模型"
|
|
||||||
name="asr_id"
|
|
||||||
rules={[{ required: true, message: 'Please input value' }]}
|
|
||||||
initialValue={tenantIfo.asr_id}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
// style={{ width: 200 }}
|
|
||||||
onChange={handleChange}
|
|
||||||
// fieldNames={label:}
|
|
||||||
options={Object.keys(llmInfo).map((t) => {
|
|
||||||
const options = llmInfo[t]
|
|
||||||
.filter((d: any) => d.model_type === 'speech2text')
|
|
||||||
.map((d: any) => ({ label: d.llm_name, value: d.llm_name }));
|
|
||||||
return { label: t, options };
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
export default SsModal;
|
|
||||||
@ -1,65 +0,0 @@
|
|||||||
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
|
||||||
import { Modal, Table } from 'antd';
|
|
||||||
import { ColumnsType } from 'antd/es/table';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { useDispatch, useSelector } from 'umi';
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
interface DataType {
|
|
||||||
key: React.Key;
|
|
||||||
name: string;
|
|
||||||
role: string;
|
|
||||||
time: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TntModal = () => {
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const settingModel = useSelector((state: any) => state.settingModel);
|
|
||||||
const { isShowTntModal, tenantIfo, factoriesList } = settingModel;
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const loading = useOneNamespaceEffectsLoading('settingModel', [
|
|
||||||
'getTenantInfo',
|
|
||||||
]);
|
|
||||||
|
|
||||||
const columns: ColumnsType<DataType> = [
|
|
||||||
{ title: '姓名', dataIndex: 'name', key: 'name' },
|
|
||||||
{ title: '活动时间', dataIndex: 'update_date', key: 'update_date' },
|
|
||||||
{ title: '角色', dataIndex: 'role', key: 'age' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const handleCancel = () => {
|
|
||||||
dispatch({
|
|
||||||
type: 'settingModel/updateState',
|
|
||||||
payload: {
|
|
||||||
isShowTntModal: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOk = async () => {
|
|
||||||
dispatch({
|
|
||||||
type: 'settingModel/updateState',
|
|
||||||
payload: {
|
|
||||||
isShowTntModal: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
title="用户"
|
|
||||||
open={isShowTntModal}
|
|
||||||
onOk={handleOk}
|
|
||||||
onCancel={handleCancel}
|
|
||||||
>
|
|
||||||
<div className={styles.tenantIfo}>{tenantIfo.name}</div>
|
|
||||||
<Table
|
|
||||||
rowKey="name"
|
|
||||||
loading={loading}
|
|
||||||
columns={columns}
|
|
||||||
dataSource={factoriesList}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
export default TntModal;
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
.settingPage {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tenantIfo {
|
|
||||||
height: 50px;
|
|
||||||
background-color: #f4dfdf;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
padding: 5px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list {
|
|
||||||
:global {
|
|
||||||
.ant-pro-card-header {
|
|
||||||
height: 150px;
|
|
||||||
background-color: rgb(229, 231, 235);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
li {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
|
|
||||||
.statusDisaabled {
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
border-radius: 40%;
|
|
||||||
background: rgba(0, 0, 0, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.statusAvailable {
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: green;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,98 +0,0 @@
|
|||||||
import { Button, FloatButton } from 'antd';
|
|
||||||
import i18n from 'i18next';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
import authorizationUtil from '@/utils/authorizationUtil';
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
import { useDispatch, useSelector } from 'umi';
|
|
||||||
import CPwModal from './CPwModal';
|
|
||||||
import List from './List';
|
|
||||||
import SAKModal from './SAKModal';
|
|
||||||
import SSModal from './SSModal';
|
|
||||||
import TntModal from './TntModal';
|
|
||||||
import styles from './index.less';
|
|
||||||
|
|
||||||
const Setting = () => {
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const settingModel = useSelector((state: any) => state.settingModel);
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const userInfo = authorizationUtil.getUserInfoObject();
|
|
||||||
|
|
||||||
const changeLang = (val: string) => {
|
|
||||||
// 改变状态里的 语言 进行切换
|
|
||||||
i18n.changeLanguage(val);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
dispatch({
|
|
||||||
type: 'settingModel/getTenantInfo',
|
|
||||||
payload: {},
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const showCPwModal = () => {
|
|
||||||
dispatch({
|
|
||||||
type: 'settingModel/updateState',
|
|
||||||
payload: {
|
|
||||||
isShowPSwModal: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const showTntModal = () => {
|
|
||||||
dispatch({
|
|
||||||
type: 'settingModel/updateState',
|
|
||||||
payload: {
|
|
||||||
isShowTntModal: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const showSSModal = () => {
|
|
||||||
dispatch({
|
|
||||||
type: 'settingModel/updateState',
|
|
||||||
payload: {
|
|
||||||
isShowSSModal: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<div className={styles.settingPage}>
|
|
||||||
<div className={styles.avatar}>
|
|
||||||
<img
|
|
||||||
style={{ width: 50, marginRight: 5 }}
|
|
||||||
src="https://os.alipayobjects.com/rmsportal/QBnOOoLaAfKPirc.png"
|
|
||||||
alt=""
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<div>账号:{userInfo.name}</div>
|
|
||||||
<div>
|
|
||||||
<span>密码:******</span>
|
|
||||||
<Button type="link" onClick={showCPwModal}>
|
|
||||||
修改密码
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Button type="link" onClick={showTntModal}>
|
|
||||||
租户
|
|
||||||
</Button>
|
|
||||||
<Button type="link" onClick={showSSModal}>
|
|
||||||
系统模型设置
|
|
||||||
</Button>
|
|
||||||
<List />
|
|
||||||
</div>
|
|
||||||
<CPwModal />
|
|
||||||
<SAKModal />
|
|
||||||
<SSModal />
|
|
||||||
<TntModal />
|
|
||||||
<FloatButton
|
|
||||||
shape="square"
|
|
||||||
description={t('setting.btn')}
|
|
||||||
onClick={() => i18n.changeLanguage(i18n.language == 'en' ? 'zh' : 'en')}
|
|
||||||
type="default"
|
|
||||||
style={{ right: 94, fontSize: 14 }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
export default Setting;
|
|
||||||
@ -1,151 +1,151 @@
|
|||||||
import { ITenantInfo } from '@/interfaces/database/knowledge';
|
import { ITenantInfo } from '@/interfaces/database/knowledge';
|
||||||
import {
|
import {
|
||||||
IFactory,
|
IFactory,
|
||||||
IMyLlmValue,
|
IMyLlmValue,
|
||||||
IThirdOAIModelCollection as IThirdAiModelCollection,
|
IThirdOAIModelCollection as IThirdAiModelCollection,
|
||||||
} from '@/interfaces/database/llm';
|
} from '@/interfaces/database/llm';
|
||||||
import { IUserInfo } from '@/interfaces/database/userSetting';
|
import { IUserInfo } from '@/interfaces/database/userSetting';
|
||||||
import userService from '@/services/userService';
|
import userService from '@/services/userService';
|
||||||
import { message } from 'antd';
|
import { message } from 'antd';
|
||||||
import { DvaModel } from 'umi';
|
import { DvaModel } from 'umi';
|
||||||
|
|
||||||
export interface SettingModelState {
|
export interface SettingModelState {
|
||||||
llm_factory: string;
|
llm_factory: string;
|
||||||
tenantIfo: Nullable<ITenantInfo>;
|
tenantIfo: Nullable<ITenantInfo>;
|
||||||
llmInfo: IThirdAiModelCollection;
|
llmInfo: IThirdAiModelCollection;
|
||||||
myLlmList: Record<string, IMyLlmValue>;
|
myLlmList: Record<string, IMyLlmValue>;
|
||||||
factoryList: IFactory[];
|
factoryList: IFactory[];
|
||||||
userInfo: IUserInfo;
|
userInfo: IUserInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
const model: DvaModel<SettingModelState> = {
|
const model: DvaModel<SettingModelState> = {
|
||||||
namespace: 'settingModel',
|
namespace: 'settingModel',
|
||||||
state: {
|
state: {
|
||||||
llm_factory: '',
|
llm_factory: '',
|
||||||
tenantIfo: null,
|
tenantIfo: null,
|
||||||
llmInfo: {},
|
llmInfo: {},
|
||||||
myLlmList: {},
|
myLlmList: {},
|
||||||
factoryList: [],
|
factoryList: [],
|
||||||
userInfo: {} as IUserInfo,
|
userInfo: {} as IUserInfo,
|
||||||
},
|
},
|
||||||
reducers: {
|
reducers: {
|
||||||
updateState(state, { payload }) {
|
updateState(state, { payload }) {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
...payload,
|
...payload,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
setUserInfo(state, { payload }) {
|
setUserInfo(state, { payload }) {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
userInfo: payload,
|
userInfo: payload,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
effects: {
|
effects: {
|
||||||
*setting({ payload = {} }, { call, put }) {
|
*setting({ payload = {} }, { call, put }) {
|
||||||
const { data } = yield call(userService.setting, payload);
|
const { data } = yield call(userService.setting, payload);
|
||||||
const { retcode } = data;
|
const { retcode } = data;
|
||||||
if (retcode === 0) {
|
if (retcode === 0) {
|
||||||
message.success('Modified!');
|
message.success('Modified!');
|
||||||
yield put({
|
yield put({
|
||||||
type: 'getUserInfo',
|
type: 'getUserInfo',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
*getUserInfo({ payload = {} }, { call, put }) {
|
*getUserInfo({ payload = {} }, { call, put }) {
|
||||||
const { data } = yield call(userService.user_info, payload);
|
const { data } = yield call(userService.user_info, payload);
|
||||||
const { retcode, data: res } = data;
|
const { retcode, data: res } = data;
|
||||||
|
|
||||||
// const userInfo = {
|
// const userInfo = {
|
||||||
// avatar: res.avatar,
|
// avatar: res.avatar,
|
||||||
// name: res.nickname,
|
// name: res.nickname,
|
||||||
// email: res.email,
|
// email: res.email,
|
||||||
// };
|
// };
|
||||||
// authorizationUtil.setUserInfo(userInfo);
|
// authorizationUtil.setUserInfo(userInfo);
|
||||||
if (retcode === 0) {
|
if (retcode === 0) {
|
||||||
yield put({ type: 'setUserInfo', payload: res });
|
yield put({ type: 'setUserInfo', payload: res });
|
||||||
// localStorage.setItem('userInfo',res.)
|
// localStorage.setItem('userInfo',res.)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
*getTenantInfo({ payload = {} }, { call, put }) {
|
*getTenantInfo({ payload = {} }, { call, put }) {
|
||||||
const { data } = yield call(userService.get_tenant_info, payload);
|
const { data } = yield call(userService.get_tenant_info, payload);
|
||||||
const { retcode, data: res } = data;
|
const { retcode, data: res } = data;
|
||||||
// llm_id 对应chat_id
|
// llm_id 对应chat_id
|
||||||
// asr_id 对应speech2txt
|
// asr_id 对应speech2txt
|
||||||
|
|
||||||
if (retcode === 0) {
|
if (retcode === 0) {
|
||||||
res.chat_id = res.llm_id;
|
res.chat_id = res.llm_id;
|
||||||
res.speech2text_id = res.asr_id;
|
res.speech2text_id = res.asr_id;
|
||||||
yield put({
|
yield put({
|
||||||
type: 'updateState',
|
type: 'updateState',
|
||||||
payload: {
|
payload: {
|
||||||
tenantIfo: res,
|
tenantIfo: res,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
*set_tenant_info({ payload = {} }, { call, put }) {
|
*set_tenant_info({ payload = {} }, { call, put }) {
|
||||||
const { data } = yield call(userService.set_tenant_info, payload);
|
const { data } = yield call(userService.set_tenant_info, payload);
|
||||||
const { retcode } = data;
|
const { retcode } = data;
|
||||||
if (retcode === 0) {
|
if (retcode === 0) {
|
||||||
message.success('Modified!');
|
message.success('Modified!');
|
||||||
yield put({
|
yield put({
|
||||||
type: 'getTenantInfo',
|
type: 'getTenantInfo',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return retcode;
|
return retcode;
|
||||||
},
|
},
|
||||||
|
|
||||||
*factories_list({ payload = {} }, { call, put }) {
|
*factories_list({ payload = {} }, { call, put }) {
|
||||||
const { data } = yield call(userService.factories_list);
|
const { data } = yield call(userService.factories_list);
|
||||||
const { retcode, data: res } = data;
|
const { retcode, data: res } = data;
|
||||||
if (retcode === 0) {
|
if (retcode === 0) {
|
||||||
yield put({
|
yield put({
|
||||||
type: 'updateState',
|
type: 'updateState',
|
||||||
payload: {
|
payload: {
|
||||||
factoryList: res,
|
factoryList: res,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
*llm_list({ payload = {} }, { call, put }) {
|
*llm_list({ payload = {} }, { call, put }) {
|
||||||
const { data } = yield call(userService.llm_list, payload);
|
const { data } = yield call(userService.llm_list, payload);
|
||||||
const { retcode, data: res } = data;
|
const { retcode, data: res } = data;
|
||||||
if (retcode === 0) {
|
if (retcode === 0) {
|
||||||
yield put({
|
yield put({
|
||||||
type: 'updateState',
|
type: 'updateState',
|
||||||
payload: {
|
payload: {
|
||||||
llmInfo: res,
|
llmInfo: res,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
*my_llm({ payload = {} }, { call, put }) {
|
*my_llm({ payload = {} }, { call, put }) {
|
||||||
const { data } = yield call(userService.my_llm);
|
const { data } = yield call(userService.my_llm);
|
||||||
const { retcode, data: res } = data;
|
const { retcode, data: res } = data;
|
||||||
if (retcode === 0) {
|
if (retcode === 0) {
|
||||||
yield put({
|
yield put({
|
||||||
type: 'updateState',
|
type: 'updateState',
|
||||||
payload: {
|
payload: {
|
||||||
myLlmList: res,
|
myLlmList: res,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
*set_api_key({ payload = {} }, { call, put }) {
|
*set_api_key({ payload = {} }, { call, put }) {
|
||||||
const { data } = yield call(userService.set_api_key, payload);
|
const { data } = yield call(userService.set_api_key, payload);
|
||||||
const { retcode } = data;
|
const { retcode } = data;
|
||||||
if (retcode === 0) {
|
if (retcode === 0) {
|
||||||
message.success('Modified!');
|
message.success('Modified!');
|
||||||
yield put({ type: 'my_llm' });
|
yield put({ type: 'my_llm' });
|
||||||
yield put({ type: 'factories_list' });
|
yield put({ type: 'factories_list' });
|
||||||
yield put({
|
yield put({
|
||||||
type: 'updateState',
|
type: 'updateState',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return retcode;
|
return retcode;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
export default model;
|
export default model;
|
||||||
@ -52,10 +52,6 @@ const routes = [
|
|||||||
path: '/chat',
|
path: '/chat',
|
||||||
component: '@/pages/chat',
|
component: '@/pages/chat',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/setting',
|
|
||||||
component: '@/pages/setting',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/user-setting',
|
path: '/user-setting',
|
||||||
component: '@/pages/user-setting',
|
component: '@/pages/user-setting',
|
||||||
|
|||||||