Sfoglia il codice sorgente

feat: add pages to ChunkMethodModal (#143)

tags/v0.1.0
balibabu 1 anno fa
parent
commit
2f4c71b4b4
Nessun account collegato all'indirizzo email del committer
36 ha cambiato i file con 1036 aggiunte e 1322 eliminazioni
  1. 62
    46
      web/src/assets/svg/chunk-method/law-01.svg
  2. 46
    46
      web/src/assets/svg/chunk-method/law-02.svg
  3. 0
    80
      web/src/assets/svg/chunk-method/law-03.svg
  4. 0
    81
      web/src/assets/svg/chunk-method/law-04.svg
  5. 16
    16
      web/src/assets/svg/chunk-method/manual-01.svg
  6. 16
    16
      web/src/assets/svg/chunk-method/manual-02.svg
  7. 16
    17
      web/src/assets/svg/chunk-method/manual-03.svg
  8. 16
    16
      web/src/assets/svg/chunk-method/manual-04.svg
  9. 79
    0
      web/src/assets/svg/chunk-method/one-01.svg
  10. 79
    0
      web/src/assets/svg/chunk-method/one-02.svg
  11. 80
    0
      web/src/assets/svg/chunk-method/one-03.svg
  12. 79
    0
      web/src/assets/svg/chunk-method/one-04.svg
  13. 42
    48
      web/src/assets/svg/chunk-method/paper-01.svg
  14. 42
    44
      web/src/assets/svg/chunk-method/paper-02.svg
  15. 32
    0
      web/src/components/max-token-number.tsx
  16. 7
    1
      web/src/hooks/documentHooks.ts
  17. 7
    0
      web/src/interfaces/database/knowledge.ts
  18. 12
    0
      web/src/interfaces/request/document.ts
  19. 198
    20
      web/src/pages/add-knowledge/components/knowledge-file/chunk-method-modal.tsx
  20. 25
    3
      web/src/pages/add-knowledge/components/knowledge-file/hooks.ts
  21. 4
    0
      web/src/pages/add-knowledge/components/knowledge-file/index.less
  22. 3
    1
      web/src/pages/add-knowledge/components/knowledge-file/index.tsx
  23. 7
    30
      web/src/pages/add-knowledge/components/knowledge-file/model.ts
  24. 3
    41
      web/src/pages/add-knowledge/components/knowledge-setting/configuration.tsx
  25. 0
    2
      web/src/pages/add-knowledge/components/knowledge-setting/model.ts
  26. 14
    11
      web/src/pages/add-knowledge/components/knowledge-setting/utils.ts
  27. 0
    2
      web/src/pages/add-knowledge/model.ts
  28. 0
    78
      web/src/pages/setting/CPwModal.tsx
  29. 0
    146
      web/src/pages/setting/List.tsx
  30. 0
    66
      web/src/pages/setting/SAKModal.tsx
  31. 0
    144
      web/src/pages/setting/SSModal.tsx
  32. 0
    65
      web/src/pages/setting/TntModal.tsx
  33. 0
    49
      web/src/pages/setting/index.less
  34. 0
    98
      web/src/pages/setting/index.tsx
  35. 151
    151
      web/src/pages/user-setting/model.ts
  36. 0
    4
      web/src/routes.ts

+ 62
- 46
web/src/assets/svg/chunk-method/law-01.svg
File diff soppresso perché troppo grande
Vedi File


+ 46
- 46
web/src/assets/svg/chunk-method/law-02.svg
File diff soppresso perché troppo grande
Vedi File


+ 0
- 80
web/src/assets/svg/chunk-method/law-03.svg
File diff soppresso perché troppo grande
Vedi File


+ 0
- 81
web/src/assets/svg/chunk-method/law-04.svg
File diff soppresso perché troppo grande
Vedi File


+ 16
- 16
web/src/assets/svg/chunk-method/manual-01.svg
File diff soppresso perché troppo grande
Vedi File


+ 16
- 16
web/src/assets/svg/chunk-method/manual-02.svg
File diff soppresso perché troppo grande
Vedi File


+ 16
- 17
web/src/assets/svg/chunk-method/manual-03.svg
File diff soppresso perché troppo grande
Vedi File


+ 16
- 16
web/src/assets/svg/chunk-method/manual-04.svg
File diff soppresso perché troppo grande
Vedi File


+ 79
- 0
web/src/assets/svg/chunk-method/one-01.svg
File diff soppresso perché troppo grande
Vedi File


+ 79
- 0
web/src/assets/svg/chunk-method/one-02.svg
File diff soppresso perché troppo grande
Vedi File


+ 80
- 0
web/src/assets/svg/chunk-method/one-03.svg
File diff soppresso perché troppo grande
Vedi File


+ 79
- 0
web/src/assets/svg/chunk-method/one-04.svg
File diff soppresso perché troppo grande
Vedi File


+ 42
- 48
web/src/assets/svg/chunk-method/paper-01.svg
File diff soppresso perché troppo grande
Vedi File


+ 42
- 44
web/src/assets/svg/chunk-method/paper-02.svg
File diff soppresso perché troppo grande
Vedi File


+ 32
- 0
web/src/components/max-token-number.tsx Vedi File

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;

+ 7
- 1
web/src/hooks/documentHooks.ts Vedi File

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';
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',
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) {

+ 7
- 0
web/src/interfaces/database/knowledge.ts Vedi File

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;
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
- 0
web/src/interfaces/request/document.ts Vedi File

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;
}

+ 198
- 20
web/src/pages/add-knowledge/components/knowledge-file/chunk-method-modal.tsx Vedi File

import { IModalManagerChildrenProps } from '@/components/modal-manager'; import { IModalManagerChildrenProps } from '@/components/modal-manager';
import { import {
useFetchTenantInfo,
useSelectParserList,
} from '@/hooks/userSettingHook';
import { Modal, Space, Tag } from 'antd';
import React, { useEffect, useState } from 'react';
Button,
Divider,
Form,
InputNumber,
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';




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 = useSelectParserList();

useFetchTenantInfo();

useEffect(() => {
setSelectedTag(parser_id);
}, [parser_id]);
const { parserList, handleChange, selectedTag } =
useFetchParserListOnMount(parserId);
const [form] = Form.useForm();


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 nextSelectedTag = checked ? tag : selectedTag;
setSelectedTag(nextSelectedTag);
const showPages = useMemo(() => {
return (
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}>
})} })}
</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>
); );
}; };

+ 25
- 3
web/src/pages/add-knowledge/components/knowledge-file/hooks.ts Vedi File

} 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';
]); ]);


const onChangeParserOk = useCallback( const onChangeParserOk = useCallback(
async (parserId: string) => {
const ret = await setDocumentParser(parserId, documentId);
async (parserId: string, parserConfig: IChangeParserConfigRequestBody) => {
const ret = await setDocumentParser(parserId, documentId, parserConfig);
if (ret === 0) { if (ret === 0) {
hideChangeParserModal(); hideChangeParserModal();
} }
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 };
};

+ 4
- 0
web/src/pages/add-knowledge/components/knowledge-file/index.less Vedi File

.tochunks { .tochunks {
cursor: pointer; cursor: pointer;
} }
.pageInputNumber {
width: 220px;
}

+ 3
- 1
web/src/pages/add-knowledge/components/knowledge-file/index.tsx Vedi File

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
- 30
web/src/pages/add-knowledge/components/knowledge-file/model.ts Vedi File

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;
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,
...payload, ...payload,
}; };
}, },
setIsShowRenameModal(state, { payload }) {
return { ...state, isShowRenameModal: payload };
},
setCurrentRecord(state, { payload }) { setCurrentRecord(state, { payload }) {
return { ...state, currentRecord: payload }; return { ...state, currentRecord: payload };
}, },
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 },
}); });
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 },
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;
); );
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;

+ 3
- 41
web/src/pages/add-knowledge/components/knowledge-setting/configuration.tsx Vedi File

import { normFile } from '@/utils/fileUtil'; import { normFile } from '@/utils/fileUtil';
import { PlusOutlined } from '@ant-design/icons'; import { PlusOutlined } from '@ant-design/icons';
import {
Button,
Flex,
Form,
Input,
InputNumber,
Radio,
Select,
Slider,
Space,
Upload,
} from 'antd';
import { Button, Form, Input, Radio, Select, 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';


const parserId = getFieldValue('parser_id'); const parserId = getFieldValue('parser_id');


if (parserId === 'naive') { if (parserId === 'naive') {
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 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 <MaxTokenNumber></MaxTokenNumber>;
} }
return null; return null;
}} }}

+ 0
- 2
web/src/pages/add-knowledge/components/knowledge-setting/model.ts Vedi File

export interface KSModelState { export interface KSModelState {
isShowPSwModal: boolean; isShowPSwModal: boolean;
isShowTntModal: boolean;
tenantIfo: any; tenantIfo: any;
knowledgeDetails: IKnowledge; knowledgeDetails: IKnowledge;
} }
namespace: 'kSModel', namespace: 'kSModel',
state: { state: {
isShowPSwModal: false, isShowPSwModal: false,
isShowTntModal: false,
tenantIfo: {}, tenantIfo: {},
knowledgeDetails: {} as any, knowledgeDetails: {} as any,
}, },

+ 14
- 11
web/src/pages/add-knowledge/components/knowledge-setting/utils.ts Vedi File



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),
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>
</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: {
title: '',
description: `
},
picture: {
title: '',
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: {
title: '',
description: `
one: {
title: '',
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.

+ 0
- 2
web/src/pages/add-knowledge/model.ts Vedi File

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;
namespace: 'kAModel', namespace: 'kAModel',
state: { state: {
isShowPSwModal: false, isShowPSwModal: false,
isShowTntModal: false,
tenantIfo: {}, tenantIfo: {},
id: '', id: '',
doc_id: '', doc_id: '',

+ 0
- 78
web/src/pages/setting/CPwModal.tsx Vedi File

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;

+ 0
- 146
web/src/pages/setting/List.tsx Vedi File

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;

+ 0
- 66
web/src/pages/setting/SAKModal.tsx Vedi File

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;

+ 0
- 144
web/src/pages/setting/SSModal.tsx Vedi File

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;

+ 0
- 65
web/src/pages/setting/TntModal.tsx Vedi File

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;

+ 0
- 49
web/src/pages/setting/index.less Vedi File

.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;
}
}
}

+ 0
- 98
web/src/pages/setting/index.tsx Vedi File

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;

web/src/pages/setting/model.ts → web/src/pages/user-setting/model.ts Vedi File

import { ITenantInfo } from '@/interfaces/database/knowledge';
import {
IFactory,
IMyLlmValue,
IThirdOAIModelCollection as IThirdAiModelCollection,
} from '@/interfaces/database/llm';
import { IUserInfo } from '@/interfaces/database/userSetting';
import userService from '@/services/userService';
import { message } from 'antd';
import { DvaModel } from 'umi';
export interface SettingModelState {
llm_factory: string;
tenantIfo: Nullable<ITenantInfo>;
llmInfo: IThirdAiModelCollection;
myLlmList: Record<string, IMyLlmValue>;
factoryList: IFactory[];
userInfo: IUserInfo;
}
const model: DvaModel<SettingModelState> = {
namespace: 'settingModel',
state: {
llm_factory: '',
tenantIfo: null,
llmInfo: {},
myLlmList: {},
factoryList: [],
userInfo: {} as IUserInfo,
},
reducers: {
updateState(state, { payload }) {
return {
...state,
...payload,
};
},
setUserInfo(state, { payload }) {
return {
...state,
userInfo: payload,
};
},
},
effects: {
*setting({ payload = {} }, { call, put }) {
const { data } = yield call(userService.setting, payload);
const { retcode } = data;
if (retcode === 0) {
message.success('Modified!');
yield put({
type: 'getUserInfo',
});
}
},
*getUserInfo({ payload = {} }, { call, put }) {
const { data } = yield call(userService.user_info, payload);
const { retcode, data: res } = data;
// const userInfo = {
// avatar: res.avatar,
// name: res.nickname,
// email: res.email,
// };
// authorizationUtil.setUserInfo(userInfo);
if (retcode === 0) {
yield put({ type: 'setUserInfo', payload: res });
// localStorage.setItem('userInfo',res.)
}
},
*getTenantInfo({ payload = {} }, { call, put }) {
const { data } = yield call(userService.get_tenant_info, payload);
const { retcode, data: res } = data;
// llm_id 对应chat_id
// asr_id 对应speech2txt
if (retcode === 0) {
res.chat_id = res.llm_id;
res.speech2text_id = res.asr_id;
yield put({
type: 'updateState',
payload: {
tenantIfo: res,
},
});
}
},
*set_tenant_info({ payload = {} }, { call, put }) {
const { data } = yield call(userService.set_tenant_info, payload);
const { retcode } = data;
if (retcode === 0) {
message.success('Modified!');
yield put({
type: 'getTenantInfo',
});
}
return retcode;
},
*factories_list({ payload = {} }, { call, put }) {
const { data } = yield call(userService.factories_list);
const { retcode, data: res } = data;
if (retcode === 0) {
yield put({
type: 'updateState',
payload: {
factoryList: res,
},
});
}
},
*llm_list({ payload = {} }, { call, put }) {
const { data } = yield call(userService.llm_list, payload);
const { retcode, data: res } = data;
if (retcode === 0) {
yield put({
type: 'updateState',
payload: {
llmInfo: res,
},
});
}
},
*my_llm({ payload = {} }, { call, put }) {
const { data } = yield call(userService.my_llm);
const { retcode, data: res } = data;
if (retcode === 0) {
yield put({
type: 'updateState',
payload: {
myLlmList: res,
},
});
}
},
*set_api_key({ payload = {} }, { call, put }) {
const { data } = yield call(userService.set_api_key, payload);
const { retcode } = data;
if (retcode === 0) {
message.success('Modified!');
yield put({ type: 'my_llm' });
yield put({ type: 'factories_list' });
yield put({
type: 'updateState',
});
}
return retcode;
},
},
};
export default model;
import { ITenantInfo } from '@/interfaces/database/knowledge';
import {
IFactory,
IMyLlmValue,
IThirdOAIModelCollection as IThirdAiModelCollection,
} from '@/interfaces/database/llm';
import { IUserInfo } from '@/interfaces/database/userSetting';
import userService from '@/services/userService';
import { message } from 'antd';
import { DvaModel } from 'umi';
export interface SettingModelState {
llm_factory: string;
tenantIfo: Nullable<ITenantInfo>;
llmInfo: IThirdAiModelCollection;
myLlmList: Record<string, IMyLlmValue>;
factoryList: IFactory[];
userInfo: IUserInfo;
}
const model: DvaModel<SettingModelState> = {
namespace: 'settingModel',
state: {
llm_factory: '',
tenantIfo: null,
llmInfo: {},
myLlmList: {},
factoryList: [],
userInfo: {} as IUserInfo,
},
reducers: {
updateState(state, { payload }) {
return {
...state,
...payload,
};
},
setUserInfo(state, { payload }) {
return {
...state,
userInfo: payload,
};
},
},
effects: {
*setting({ payload = {} }, { call, put }) {
const { data } = yield call(userService.setting, payload);
const { retcode } = data;
if (retcode === 0) {
message.success('Modified!');
yield put({
type: 'getUserInfo',
});
}
},
*getUserInfo({ payload = {} }, { call, put }) {
const { data } = yield call(userService.user_info, payload);
const { retcode, data: res } = data;
// const userInfo = {
// avatar: res.avatar,
// name: res.nickname,
// email: res.email,
// };
// authorizationUtil.setUserInfo(userInfo);
if (retcode === 0) {
yield put({ type: 'setUserInfo', payload: res });
// localStorage.setItem('userInfo',res.)
}
},
*getTenantInfo({ payload = {} }, { call, put }) {
const { data } = yield call(userService.get_tenant_info, payload);
const { retcode, data: res } = data;
// llm_id 对应chat_id
// asr_id 对应speech2txt
if (retcode === 0) {
res.chat_id = res.llm_id;
res.speech2text_id = res.asr_id;
yield put({
type: 'updateState',
payload: {
tenantIfo: res,
},
});
}
},
*set_tenant_info({ payload = {} }, { call, put }) {
const { data } = yield call(userService.set_tenant_info, payload);
const { retcode } = data;
if (retcode === 0) {
message.success('Modified!');
yield put({
type: 'getTenantInfo',
});
}
return retcode;
},
*factories_list({ payload = {} }, { call, put }) {
const { data } = yield call(userService.factories_list);
const { retcode, data: res } = data;
if (retcode === 0) {
yield put({
type: 'updateState',
payload: {
factoryList: res,
},
});
}
},
*llm_list({ payload = {} }, { call, put }) {
const { data } = yield call(userService.llm_list, payload);
const { retcode, data: res } = data;
if (retcode === 0) {
yield put({
type: 'updateState',
payload: {
llmInfo: res,
},
});
}
},
*my_llm({ payload = {} }, { call, put }) {
const { data } = yield call(userService.my_llm);
const { retcode, data: res } = data;
if (retcode === 0) {
yield put({
type: 'updateState',
payload: {
myLlmList: res,
},
});
}
},
*set_api_key({ payload = {} }, { call, put }) {
const { data } = yield call(userService.set_api_key, payload);
const { retcode } = data;
if (retcode === 0) {
message.success('Modified!');
yield put({ type: 'my_llm' });
yield put({ type: 'factories_list' });
yield put({
type: 'updateState',
});
}
return retcode;
},
},
};
export default model;

+ 0
- 4
web/src/routes.ts Vedi File

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',

Loading…
Annulla
Salva