| @@ -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 { IChangeParserConfigRequestBody } from '@/interfaces/request/document'; | |||
| import { api_host } from '@/utils/api'; | |||
| import { buildChunkHighlights } from '@/utils/documentUtils'; | |||
| import { useCallback, useMemo } from 'react'; | |||
| @@ -117,7 +118,11 @@ export const useSetDocumentParser = () => { | |||
| const { knowledgeId } = useGetKnowledgeSearchParams(); | |||
| const setDocumentParser = useCallback( | |||
| (parserId: string, documentId: string) => { | |||
| ( | |||
| parserId: string, | |||
| documentId: string, | |||
| parserConfig: IChangeParserConfigRequestBody, | |||
| ) => { | |||
| try { | |||
| return dispatch<any>({ | |||
| type: 'kFModel/document_change_parser', | |||
| @@ -125,6 +130,7 @@ export const useSetDocumentParser = () => { | |||
| parser_id: parserId, | |||
| doc_id: documentId, | |||
| kb_id: knowledgeId, | |||
| parser_config: parserConfig, | |||
| }, | |||
| }); | |||
| } catch (errorInfo) { | |||
| @@ -28,6 +28,12 @@ export interface Parserconfig { | |||
| to_page: number; | |||
| } | |||
| export interface IKnowledgeFileParserConfig { | |||
| chunk_token_num: number; | |||
| layout_recognize: boolean; | |||
| pages: number[][]; | |||
| task_page_size: number; | |||
| } | |||
| export interface IKnowledgeFile { | |||
| chunk_num: number; | |||
| create_date: string; | |||
| @@ -51,6 +57,7 @@ export interface IKnowledgeFile { | |||
| type: string; | |||
| update_date: string; | |||
| update_time: number; | |||
| parser_config: IKnowledgeFileParserConfig; | |||
| } | |||
| export interface ITenantInfo { | |||
| @@ -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 { | |||
| 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'; | |||
| @@ -12,41 +25,74 @@ const { CheckableTag } = Tag; | |||
| interface IProps extends Omit<IModalManagerChildrenProps, 'showModal'> { | |||
| loading: boolean; | |||
| onOk: (parserId: string) => void; | |||
| onOk: ( | |||
| parserId: string, | |||
| parserConfig: IChangeParserConfigRequestBody, | |||
| ) => void; | |||
| showModal?(): void; | |||
| parser_id: string; | |||
| parserId: string; | |||
| parserConfig: IKnowledgeFileParserConfig; | |||
| documentType: string; | |||
| } | |||
| const hidePagesChunkMethods = ['qa', 'table', 'picture', 'resume', 'one']; | |||
| const ChunkMethodModal: React.FC<IProps> = ({ | |||
| parser_id, | |||
| parserId, | |||
| onOk, | |||
| hideModal, | |||
| 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 () => { | |||
| 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 ( | |||
| <Modal | |||
| title="Chunk Method" | |||
| open={visible} | |||
| onOk={handleOk} | |||
| onCancel={hideModal} | |||
| afterClose={afterClose} | |||
| > | |||
| <Space size={[0, 8]} wrap> | |||
| <div className={styles.tags}> | |||
| @@ -63,6 +109,138 @@ const ChunkMethodModal: React.FC<IProps> = ({ | |||
| })} | |||
| </div> | |||
| </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> | |||
| ); | |||
| }; | |||
| @@ -7,9 +7,13 @@ import { | |||
| } from '@/hooks/documentHooks'; | |||
| import { useGetKnowledgeSearchParams } from '@/hooks/routeHook'; | |||
| import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks'; | |||
| import { useFetchTenantInfo } from '@/hooks/userSettingHook'; | |||
| import { | |||
| useFetchTenantInfo, | |||
| useSelectParserList, | |||
| } from '@/hooks/userSettingHook'; | |||
| import { Pagination } from '@/interfaces/common'; | |||
| import { IKnowledgeFile } from '@/interfaces/database/knowledge'; | |||
| import { IChangeParserConfigRequestBody } from '@/interfaces/request/document'; | |||
| import { PaginationProps } from 'antd'; | |||
| import { useCallback, useEffect, useMemo, useState } from 'react'; | |||
| import { useDispatch, useNavigate, useSelector } from 'umi'; | |||
| @@ -222,8 +226,8 @@ export const useChangeDocumentParser = (documentId: string) => { | |||
| ]); | |||
| 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) { | |||
| hideChangeParserModal(); | |||
| } | |||
| @@ -239,3 +243,21 @@ export const useChangeDocumentParser = (documentId: string) => { | |||
| 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 { | |||
| cursor: pointer; | |||
| } | |||
| .pageInputNumber { | |||
| width: 220px; | |||
| } | |||
| @@ -224,7 +224,9 @@ const KnowledgeFile = () => { | |||
| onOk={onCreateOk} | |||
| /> | |||
| <ChunkMethodModal | |||
| parser_id={currentRecord.parser_id} | |||
| parserId={currentRecord.parser_id} | |||
| parserConfig={currentRecord.parser_config} | |||
| documentType={currentRecord.type} | |||
| onOk={onChangeParserOk} | |||
| visible={changeParserVisible} | |||
| hideModal={hideChangeParserModal} | |||
| @@ -7,10 +7,6 @@ import pick from 'lodash/pick'; | |||
| import { DvaModel } from 'umi'; | |||
| export interface KFModelState extends BaseState { | |||
| isShowCEFwModal: boolean; | |||
| isShowTntModal: boolean; | |||
| isShowSegmentSetModal: boolean; | |||
| isShowRenameModal: boolean; | |||
| tenantIfo: any; | |||
| data: IKnowledgeFile[]; | |||
| total: number; | |||
| @@ -21,10 +17,6 @@ export interface KFModelState extends BaseState { | |||
| const model: DvaModel<KFModelState> = { | |||
| namespace: 'kFModel', | |||
| state: { | |||
| isShowCEFwModal: false, | |||
| isShowTntModal: false, | |||
| isShowSegmentSetModal: false, | |||
| isShowRenameModal: false, | |||
| tenantIfo: {}, | |||
| data: [], | |||
| total: 0, | |||
| @@ -43,9 +35,7 @@ const model: DvaModel<KFModelState> = { | |||
| ...payload, | |||
| }; | |||
| }, | |||
| setIsShowRenameModal(state, { payload }) { | |||
| return { ...state, isShowRenameModal: payload }; | |||
| }, | |||
| setCurrentRecord(state, { payload }) { | |||
| return { ...state, currentRecord: payload }; | |||
| }, | |||
| @@ -120,7 +110,7 @@ const model: DvaModel<KFModelState> = { | |||
| const { retcode } = data; | |||
| if (retcode === 0) { | |||
| message.success('Modified!'); | |||
| put({ | |||
| yield put({ | |||
| type: 'getKfList', | |||
| payload: { kb_id: payload.kb_id }, | |||
| }); | |||
| @@ -148,10 +138,7 @@ const model: DvaModel<KFModelState> = { | |||
| const { retcode } = data; | |||
| if (retcode === 0) { | |||
| message.success('rename success!'); | |||
| yield put({ | |||
| type: 'setIsShowRenameModal', | |||
| payload: false, | |||
| }); | |||
| yield put({ | |||
| type: 'getKfList', | |||
| payload: { kb_id: payload.kb_id }, | |||
| @@ -164,16 +151,11 @@ const model: DvaModel<KFModelState> = { | |||
| const { data } = yield call(kbService.document_create, payload); | |||
| const { retcode } = data; | |||
| if (retcode === 0) { | |||
| put({ | |||
| yield put({ | |||
| type: 'getKfList', | |||
| payload: { kb_id: payload.kb_id }, | |||
| }); | |||
| put({ | |||
| type: 'kFModel/updateState', | |||
| payload: { | |||
| isShowCEFwModal: false, | |||
| }, | |||
| }); | |||
| message.success('Created!'); | |||
| } | |||
| return retcode; | |||
| @@ -202,16 +184,11 @@ const model: DvaModel<KFModelState> = { | |||
| ); | |||
| const { retcode } = data; | |||
| if (retcode === 0) { | |||
| put({ | |||
| yield put({ | |||
| type: 'getKfList', | |||
| payload: { kb_id: payload.kb_id }, | |||
| }); | |||
| put({ | |||
| type: 'updateState', | |||
| payload: { | |||
| isShowSegmentSetModal: false, | |||
| }, | |||
| }); | |||
| message.success('Modified!'); | |||
| } | |||
| return retcode; | |||
| @@ -1,22 +1,12 @@ | |||
| import { normFile } from '@/utils/fileUtil'; | |||
| 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 { | |||
| useFetchKnowledgeConfigurationOnMount, | |||
| useSubmitKnowledgeConfiguration, | |||
| } from './hooks'; | |||
| import MaxTokenNumber from '@/components/max-token-number'; | |||
| import { FormInstance } from 'antd/lib'; | |||
| import styles from './index.less'; | |||
| @@ -121,35 +111,7 @@ const ConfigurationForm = ({ form }: { form: FormInstance }) => { | |||
| const parserId = getFieldValue('parser_id'); | |||
| 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; | |||
| }} | |||
| @@ -5,7 +5,6 @@ import { DvaModel } from 'umi'; | |||
| export interface KSModelState { | |||
| isShowPSwModal: boolean; | |||
| isShowTntModal: boolean; | |||
| tenantIfo: any; | |||
| knowledgeDetails: IKnowledge; | |||
| } | |||
| @@ -14,7 +13,6 @@ const model: DvaModel<KSModelState> = { | |||
| namespace: 'kSModel', | |||
| state: { | |||
| isShowPSwModal: false, | |||
| isShowTntModal: false, | |||
| tenantIfo: {}, | |||
| knowledgeDetails: {} as any, | |||
| }, | |||
| @@ -5,9 +5,9 @@ const getImageName = (prefix: string, length: number) => | |||
| export const ImageMap = { | |||
| book: getImageName('book', 4), | |||
| laws: getImageName('law', 4), | |||
| laws: getImageName('law', 2), | |||
| manual: getImageName('manual', 4), | |||
| picture: getImageName('picture', 2), | |||
| picture: getImageName('media', 2), | |||
| naive: getImageName('naive', 2), | |||
| paper: getImageName('paper', 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. | |||
| </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. | |||
| So, the figures and tables in the same section will not be sliced apart, and chunk size might be large. | |||
| </p>` }, | |||
| </p>`, | |||
| }, | |||
| naive: { | |||
| title: '', | |||
| description: `<p>Supported file formats are <b>DOCX, EXCEL, PPT, IMAGE, PDF, TXT</b>.</p> | |||
| @@ -100,19 +103,19 @@ export const TextMap = { | |||
| </li> | |||
| <li>Every row in table will be treated as a chunk.</li> | |||
| </ul>`, | |||
| }, | |||
| picture: { | |||
| title: '', | |||
| description: ` | |||
| }, | |||
| picture: { | |||
| title: '', | |||
| description: ` | |||
| <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. | |||
| </p><p> | |||
| If the text extracted by OCR is not enough, visual LLM is used to get the descriptions. | |||
| </p>`, | |||
| }, | |||
| one: { | |||
| title: '', | |||
| description: ` | |||
| one: { | |||
| title: '', | |||
| description: ` | |||
| <p>Supported file formats are <b>DOCX, EXCEL, PDF, TXT</b>. | |||
| </p><p> | |||
| For a document, it will be treated as an entire chunk, no split at all. | |||
| @@ -1,7 +1,6 @@ | |||
| import { DvaModel } from 'umi'; | |||
| export interface kAModelState { | |||
| isShowPSwModal: boolean; | |||
| isShowTntModal: boolean; | |||
| tenantIfo: any; | |||
| id: string; | |||
| doc_id: string; | |||
| @@ -11,7 +10,6 @@ const model: DvaModel<kAModelState> = { | |||
| namespace: 'kAModel', | |||
| state: { | |||
| isShowPSwModal: false, | |||
| isShowTntModal: false, | |||
| tenantIfo: {}, | |||
| 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 { | |||
| 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; | |||
| @@ -52,10 +52,6 @@ const routes = [ | |||
| path: '/chat', | |||
| component: '@/pages/chat', | |||
| }, | |||
| { | |||
| path: '/setting', | |||
| component: '@/pages/setting', | |||
| }, | |||
| { | |||
| path: '/user-setting', | |||
| component: '@/pages/user-setting', | |||