balibabu commited on
Commit
7e26e0d
·
1 Parent(s): 6be8543

feat: submit api key and add language to Configuration and fetch llm factory list on UserSettingModel mount (#121)

Browse files

* feat: fetch llm factory list on UserSettingModel mount

* feat: add language to Configuration

* feat: submit api key

web/src/hooks/llmHooks.ts CHANGED
@@ -1,5 +1,9 @@
1
  import { LlmModelType } from '@/constants/knowledge';
2
- import { IThirdOAIModelCollection } from '@/interfaces/database/llm';
 
 
 
 
3
  import { useCallback, useEffect, useMemo } from 'react';
4
  import { useDispatch, useSelector } from 'umi';
5
 
@@ -38,3 +42,96 @@ export const useSelectLlmOptions = () => {
38
 
39
  return embeddingModelOptions;
40
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import { LlmModelType } from '@/constants/knowledge';
2
+ import {
3
+ IFactory,
4
+ IMyLlmValue,
5
+ IThirdOAIModelCollection,
6
+ } from '@/interfaces/database/llm';
7
  import { useCallback, useEffect, useMemo } from 'react';
8
  import { useDispatch, useSelector } from 'umi';
9
 
 
42
 
43
  return embeddingModelOptions;
44
  };
45
+
46
+ export const useSelectLlmFactoryList = () => {
47
+ const factoryList: IFactory[] = useSelector(
48
+ (state: any) => state.settingModel.factoryList,
49
+ );
50
+
51
+ return factoryList;
52
+ };
53
+
54
+ export const useSelectMyLlmList = () => {
55
+ const myLlmList: Record<string, IMyLlmValue> = useSelector(
56
+ (state: any) => state.settingModel.myLlmList,
57
+ );
58
+
59
+ return myLlmList;
60
+ };
61
+
62
+ export const useFetchLlmFactoryListOnMount = () => {
63
+ const dispatch = useDispatch();
64
+ const factoryList = useSelectLlmFactoryList();
65
+ const myLlmList = useSelectMyLlmList();
66
+
67
+ const list = useMemo(
68
+ () =>
69
+ factoryList.filter((x) =>
70
+ Object.keys(myLlmList).every((y) => y !== x.name),
71
+ ),
72
+ [factoryList, myLlmList],
73
+ );
74
+
75
+ const fetchLlmFactoryList = useCallback(() => {
76
+ dispatch({
77
+ type: 'settingModel/factories_list',
78
+ });
79
+ }, [dispatch]);
80
+
81
+ useEffect(() => {
82
+ fetchLlmFactoryList();
83
+ }, [fetchLlmFactoryList]);
84
+
85
+ return list;
86
+ };
87
+
88
+ export const useFetchMyLlmListOnMount = () => {
89
+ const dispatch = useDispatch();
90
+ const llmList = useSelectMyLlmList();
91
+ const factoryList = useSelectLlmFactoryList();
92
+
93
+ const list: Array<{ name: string; logo: string } & IMyLlmValue> =
94
+ useMemo(() => {
95
+ return Object.entries(llmList).map(([key, value]) => ({
96
+ name: key,
97
+ logo: factoryList.find((x) => x.name === key)?.logo ?? '',
98
+ ...value,
99
+ }));
100
+ }, [llmList, factoryList]);
101
+
102
+ const fetchMyLlmList = useCallback(() => {
103
+ dispatch({
104
+ type: 'settingModel/my_llm',
105
+ });
106
+ }, [dispatch]);
107
+
108
+ useEffect(() => {
109
+ fetchMyLlmList();
110
+ }, [fetchMyLlmList]);
111
+
112
+ return list;
113
+ };
114
+
115
+ export interface IApiKeySavingParams {
116
+ llm_factory: string;
117
+ api_key: string;
118
+ llm_name?: string;
119
+ model_type?: string;
120
+ api_base?: string;
121
+ }
122
+
123
+ export const useSaveApiKey = () => {
124
+ const dispatch = useDispatch();
125
+
126
+ const saveApiKey = useCallback(
127
+ (savingParams: IApiKeySavingParams) => {
128
+ return dispatch<any>({
129
+ type: 'settingModel/set_api_key',
130
+ payload: savingParams,
131
+ });
132
+ },
133
+ [dispatch],
134
+ );
135
+
136
+ return saveApiKey;
137
+ };
web/src/interfaces/database/llm.ts CHANGED
@@ -14,3 +14,25 @@ export interface IThirdOAIModel {
14
  }
15
 
16
  export type IThirdOAIModelCollection = Record<string, IThirdOAIModel[]>;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  }
15
 
16
  export type IThirdOAIModelCollection = Record<string, IThirdOAIModel[]>;
17
+
18
+ export interface IFactory {
19
+ create_date: string;
20
+ create_time: number;
21
+ logo: string;
22
+ name: string;
23
+ status: string;
24
+ tags: string;
25
+ update_date: string;
26
+ update_time: number;
27
+ }
28
+
29
+ export interface IMyLlmValue {
30
+ llm: Llm[];
31
+ tags: string;
32
+ }
33
+
34
+ export interface Llm {
35
+ name: string;
36
+ type: string;
37
+ used_token: number;
38
+ }
web/src/pages/add-knowledge/components/knowledge-setting/configuration.tsx CHANGED
@@ -2,11 +2,19 @@ import {
2
  useFetchKnowledgeBaseConfiguration,
3
  useKnowledgeBaseId,
4
  } from '@/hooks/knowledgeHook';
 
 
5
  import {
6
  useFetchTenantInfo,
7
  useSelectParserList,
8
  } from '@/hooks/userSettingHook';
9
-
 
 
 
 
 
 
10
  import {
11
  Button,
12
  Divider,
@@ -25,17 +33,8 @@ import {
25
  import pick from 'lodash/pick';
26
  import { useEffect } from 'react';
27
  import { useDispatch, useSelector } from 'umi';
28
-
29
- import { useFetchLlmList, useSelectLlmOptions } from '@/hooks/llmHooks';
30
- import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
31
- import { IKnowledge } from '@/interfaces/database/knowledge';
32
- import {
33
- getBase64FromUploadFileList,
34
- getUploadFileListFromBase64,
35
- normFile,
36
- } from '@/utils/fileUtil';
37
- import { PlusOutlined } from '@ant-design/icons';
38
  import { LlmModelType } from '../../constant';
 
39
  import styles from './index.less';
40
 
41
  const { Title } = Typography;
@@ -83,6 +82,7 @@ const Configuration = () => {
83
  'permission',
84
  'embd_id',
85
  'parser_id',
 
86
  'parser_config.chunk_token_num',
87
  ]),
88
  avatar: fileList,
@@ -131,9 +131,20 @@ const Configuration = () => {
131
  </button>
132
  </Upload>
133
  </Form.Item>
134
- <Form.Item name="description" label="Knowledge base bio">
135
  <Input />
136
  </Form.Item>
 
 
 
 
 
 
 
 
 
 
 
137
  <Form.Item
138
  name="permission"
139
  label="Permissions"
@@ -207,7 +218,6 @@ const Configuration = () => {
207
  </Form.Item>
208
  );
209
  }
210
-
211
  return null;
212
  }}
213
  </Form.Item>
 
2
  useFetchKnowledgeBaseConfiguration,
3
  useKnowledgeBaseId,
4
  } from '@/hooks/knowledgeHook';
5
+ import { useFetchLlmList, useSelectLlmOptions } from '@/hooks/llmHooks';
6
+ import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
7
  import {
8
  useFetchTenantInfo,
9
  useSelectParserList,
10
  } from '@/hooks/userSettingHook';
11
+ import { IKnowledge } from '@/interfaces/database/knowledge';
12
+ import {
13
+ getBase64FromUploadFileList,
14
+ getUploadFileListFromBase64,
15
+ normFile,
16
+ } from '@/utils/fileUtil';
17
+ import { PlusOutlined } from '@ant-design/icons';
18
  import {
19
  Button,
20
  Divider,
 
33
  import pick from 'lodash/pick';
34
  import { useEffect } from 'react';
35
  import { useDispatch, useSelector } from 'umi';
 
 
 
 
 
 
 
 
 
 
36
  import { LlmModelType } from '../../constant';
37
+
38
  import styles from './index.less';
39
 
40
  const { Title } = Typography;
 
82
  'permission',
83
  'embd_id',
84
  'parser_id',
85
+ 'language',
86
  'parser_config.chunk_token_num',
87
  ]),
88
  avatar: fileList,
 
131
  </button>
132
  </Upload>
133
  </Form.Item>
134
+ <Form.Item name="description" label="Description">
135
  <Input />
136
  </Form.Item>
137
+ <Form.Item
138
+ label="Language"
139
+ name="language"
140
+ initialValue={'Chinese'}
141
+ rules={[{ required: true, message: 'Please input your language!' }]}
142
+ >
143
+ <Select placeholder="select your language">
144
+ <Option value="English">English</Option>
145
+ <Option value="Chinese">Chinese</Option>
146
+ </Select>
147
+ </Form.Item>
148
  <Form.Item
149
  name="permission"
150
  label="Permissions"
 
218
  </Form.Item>
219
  );
220
  }
 
221
  return null;
222
  }}
223
  </Form.Item>
web/src/pages/setting/model.ts CHANGED
@@ -1,5 +1,9 @@
1
  import { ITenantInfo } from '@/interfaces/database/knowledge';
2
- import { IThirdOAIModelCollection as IThirdAiModelCollection } from '@/interfaces/database/llm';
 
 
 
 
3
  import { IUserInfo } from '@/interfaces/database/userSetting';
4
  import userService from '@/services/userService';
5
  import { message } from 'antd';
@@ -9,13 +13,12 @@ import { DvaModel } from 'umi';
9
  export interface SettingModelState {
10
  isShowPSwModal: boolean;
11
  isShowTntModal: boolean;
12
- isShowSAKModal: boolean;
13
  isShowSSModal: boolean;
14
  llm_factory: string;
15
  tenantIfo: Nullable<ITenantInfo>;
16
  llmInfo: IThirdAiModelCollection;
17
- myLlm: any[];
18
- factoriesList: any[];
19
  userInfo: IUserInfo;
20
  }
21
 
@@ -24,13 +27,12 @@ const model: DvaModel<SettingModelState> = {
24
  state: {
25
  isShowPSwModal: false,
26
  isShowTntModal: false,
27
- isShowSAKModal: false,
28
  isShowSSModal: false,
29
  llm_factory: '',
30
  tenantIfo: null,
31
  llmInfo: {},
32
- myLlm: [],
33
- factoriesList: [],
34
  userInfo: {} as IUserInfo,
35
  },
36
  reducers: {
@@ -116,16 +118,13 @@ const model: DvaModel<SettingModelState> = {
116
  },
117
 
118
  *factories_list({ payload = {} }, { call, put }) {
119
- const { data, response } = yield call(
120
- userService.factories_list,
121
- payload,
122
- );
123
- const { retcode, data: res, retmsg } = data;
124
  if (retcode === 0) {
125
  yield put({
126
  type: 'updateState',
127
  payload: {
128
- factoriesList: res,
129
  },
130
  });
131
  }
@@ -143,13 +142,13 @@ const model: DvaModel<SettingModelState> = {
143
  }
144
  },
145
  *my_llm({ payload = {} }, { call, put }) {
146
- const { data, response } = yield call(userService.my_llm, payload);
147
- const { retcode, data: res, retmsg } = data;
148
  if (retcode === 0) {
149
  yield put({
150
  type: 'updateState',
151
  payload: {
152
- myLlm: res,
153
  },
154
  });
155
  }
@@ -158,14 +157,12 @@ const model: DvaModel<SettingModelState> = {
158
  const { data } = yield call(userService.set_api_key, payload);
159
  const { retcode } = data;
160
  if (retcode === 0) {
161
- message.success('设置API KEY成功!');
162
  yield put({
163
  type: 'updateState',
164
- payload: {
165
- isShowSAKModal: false,
166
- },
167
  });
168
  }
 
169
  },
170
  },
171
  };
 
1
  import { ITenantInfo } from '@/interfaces/database/knowledge';
2
+ import {
3
+ IFactory,
4
+ IMyLlmValue,
5
+ IThirdOAIModelCollection as IThirdAiModelCollection,
6
+ } from '@/interfaces/database/llm';
7
  import { IUserInfo } from '@/interfaces/database/userSetting';
8
  import userService from '@/services/userService';
9
  import { message } from 'antd';
 
13
  export interface SettingModelState {
14
  isShowPSwModal: boolean;
15
  isShowTntModal: boolean;
 
16
  isShowSSModal: boolean;
17
  llm_factory: string;
18
  tenantIfo: Nullable<ITenantInfo>;
19
  llmInfo: IThirdAiModelCollection;
20
+ myLlmList: Record<string, IMyLlmValue>;
21
+ factoryList: IFactory[];
22
  userInfo: IUserInfo;
23
  }
24
 
 
27
  state: {
28
  isShowPSwModal: false,
29
  isShowTntModal: false,
 
30
  isShowSSModal: false,
31
  llm_factory: '',
32
  tenantIfo: null,
33
  llmInfo: {},
34
+ myLlmList: {},
35
+ factoryList: [],
36
  userInfo: {} as IUserInfo,
37
  },
38
  reducers: {
 
118
  },
119
 
120
  *factories_list({ payload = {} }, { call, put }) {
121
+ const { data } = yield call(userService.factories_list);
122
+ const { retcode, data: res } = data;
 
 
 
123
  if (retcode === 0) {
124
  yield put({
125
  type: 'updateState',
126
  payload: {
127
+ factoryList: res,
128
  },
129
  });
130
  }
 
142
  }
143
  },
144
  *my_llm({ payload = {} }, { call, put }) {
145
+ const { data } = yield call(userService.my_llm, payload);
146
+ const { retcode, data: res } = data;
147
  if (retcode === 0) {
148
  yield put({
149
  type: 'updateState',
150
  payload: {
151
+ myLlmList: res,
152
  },
153
  });
154
  }
 
157
  const { data } = yield call(userService.set_api_key, payload);
158
  const { retcode } = data;
159
  if (retcode === 0) {
160
+ message.success('Modified!');
161
  yield put({
162
  type: 'updateState',
 
 
 
163
  });
164
  }
165
+ return retcode;
166
  },
167
  },
168
  };
web/src/pages/user-setting/setting-model/api-key-modal/index.tsx ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { IModalManagerChildrenProps } from '@/components/modal-manager';
2
+ import { Form, Input, Modal } from 'antd';
3
+ import { useEffect } from 'react';
4
+
5
+ interface IProps extends Omit<IModalManagerChildrenProps, 'showModal'> {
6
+ loading: boolean;
7
+ initialValue: string;
8
+ onOk: (name: string) => void;
9
+ showModal?(): void;
10
+ }
11
+
12
+ type FieldType = {
13
+ api_key?: string;
14
+ };
15
+
16
+ const ApiKeyModal = ({
17
+ visible,
18
+ hideModal,
19
+ loading,
20
+ initialValue,
21
+ onOk,
22
+ }: IProps) => {
23
+ const [form] = Form.useForm();
24
+
25
+ const handleOk = async () => {
26
+ const ret = await form.validateFields();
27
+
28
+ return onOk(ret.api_key);
29
+ };
30
+
31
+ const handleCancel = () => {
32
+ hideModal();
33
+ };
34
+
35
+ const onFinish = (values: any) => {
36
+ console.log('Success:', values);
37
+ };
38
+
39
+ const onFinishFailed = (errorInfo: any) => {
40
+ console.log('Failed:', errorInfo);
41
+ };
42
+
43
+ useEffect(() => {
44
+ form.setFieldValue('api_key', initialValue);
45
+ }, [initialValue, form]);
46
+
47
+ return (
48
+ <Modal
49
+ title="Modify"
50
+ open={visible}
51
+ onOk={handleOk}
52
+ onCancel={handleCancel}
53
+ okButtonProps={{ loading }}
54
+ confirmLoading={loading}
55
+ >
56
+ <Form
57
+ name="basic"
58
+ labelCol={{ span: 4 }}
59
+ wrapperCol={{ span: 20 }}
60
+ style={{ maxWidth: 600 }}
61
+ onFinish={onFinish}
62
+ onFinishFailed={onFinishFailed}
63
+ autoComplete="off"
64
+ form={form}
65
+ >
66
+ <Form.Item<FieldType>
67
+ label="Api key"
68
+ name="api_key"
69
+ rules={[{ required: true, message: 'Please input api key!' }]}
70
+ >
71
+ <Input />
72
+ </Form.Item>
73
+ </Form>
74
+ </Modal>
75
+ );
76
+ };
77
+
78
+ export default ApiKeyModal;
web/src/pages/user-setting/setting-model/hooks.ts ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useSetModalState } from '@/hooks/commonHooks';
2
+ import { IApiKeySavingParams, useSaveApiKey } from '@/hooks/llmHooks';
3
+ import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
4
+ import { useCallback, useState } from 'react';
5
+
6
+ type SavingParamsState = Omit<IApiKeySavingParams, 'api_key'>;
7
+
8
+ export const useSubmitApiKey = () => {
9
+ const [savingParams, setSavingParams] = useState<SavingParamsState>(
10
+ {} as SavingParamsState,
11
+ );
12
+ const saveApiKey = useSaveApiKey();
13
+ const {
14
+ visible: apiKeyVisible,
15
+ hideModal: hideApiKeyModal,
16
+ showModal: showApiKeyModal,
17
+ } = useSetModalState();
18
+
19
+ const onApiKeySavingOk = useCallback(
20
+ async (apiKey: string) => {
21
+ const ret = await saveApiKey({ ...savingParams, api_key: apiKey });
22
+
23
+ if (ret.retcode === 0) {
24
+ hideApiKeyModal();
25
+ }
26
+ },
27
+ [hideApiKeyModal, saveApiKey, savingParams],
28
+ );
29
+
30
+ const onShowApiKeyModal = useCallback(
31
+ (savingParams: SavingParamsState) => {
32
+ setSavingParams(savingParams);
33
+ showApiKeyModal();
34
+ },
35
+ [showApiKeyModal, setSavingParams],
36
+ );
37
+
38
+ const loading = useOneNamespaceEffectsLoading('settingModel', [
39
+ 'set_api_key',
40
+ ]);
41
+
42
+ return {
43
+ saveApiKeyLoading: loading,
44
+ initialApiKey: '',
45
+ onApiKeySavingOk,
46
+ apiKeyVisible,
47
+ hideApiKeyModal,
48
+ showApiKeyModal: onShowApiKeyModal,
49
+ };
50
+ };
web/src/pages/user-setting/setting-model/index.less ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ .modelWrapper {
2
+ width: 100%;
3
+ .factoryOperationWrapper {
4
+ text-align: right;
5
+ }
6
+ }
web/src/pages/user-setting/setting-model/index.tsx CHANGED
@@ -1,5 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  const UserSettingModel = () => {
2
- return <div>UserSettingModel</div>;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  };
4
 
5
  export default UserSettingModel;
 
1
+ import {
2
+ useFetchLlmFactoryListOnMount,
3
+ useFetchMyLlmListOnMount,
4
+ } from '@/hooks/llmHooks';
5
+ import { SettingOutlined } from '@ant-design/icons';
6
+ import {
7
+ Avatar,
8
+ Button,
9
+ Card,
10
+ Col,
11
+ Divider,
12
+ Flex,
13
+ List,
14
+ Row,
15
+ Space,
16
+ Tag,
17
+ } from 'antd';
18
+ import SettingTitle from '../components/setting-title';
19
+ import ApiKeyModal from './api-key-modal';
20
+ import { useSubmitApiKey } from './hooks';
21
+
22
+ import styles from './index.less';
23
+
24
  const UserSettingModel = () => {
25
+ const factoryList = useFetchLlmFactoryListOnMount();
26
+ const llmList = useFetchMyLlmListOnMount();
27
+ const {
28
+ saveApiKeyLoading,
29
+ initialApiKey,
30
+ onApiKeySavingOk,
31
+ apiKeyVisible,
32
+ hideApiKeyModal,
33
+ showApiKeyModal,
34
+ } = useSubmitApiKey();
35
+
36
+ const handleApiKeyClick = (llmFactory: string) => () => {
37
+ showApiKeyModal({ llm_factory: llmFactory });
38
+ };
39
+
40
+ return (
41
+ <>
42
+ <section className={styles.modelWrapper}>
43
+ <SettingTitle
44
+ title="Model Setting"
45
+ description="Manage your account settings and preferences here."
46
+ ></SettingTitle>
47
+ <Divider></Divider>
48
+ <List
49
+ grid={{ gutter: 16, column: 1 }}
50
+ dataSource={llmList}
51
+ renderItem={(item) => (
52
+ <List.Item>
53
+ <Card>
54
+ <Row align={'middle'}>
55
+ <Col span={12}>
56
+ <Flex gap={'middle'} align="center">
57
+ <Avatar shape="square" size="large" src={item.logo} />
58
+ <Flex vertical gap={'small'}>
59
+ <b>{item.name}</b>
60
+ <div>
61
+ {item.tags.split(',').map((x) => (
62
+ <Tag key={x}>{x}</Tag>
63
+ ))}
64
+ </div>
65
+ </Flex>
66
+ </Flex>
67
+ </Col>
68
+ <Col span={12} className={styles.factoryOperationWrapper}>
69
+ <Space size={'middle'}>
70
+ <Button onClick={handleApiKeyClick(item.name)}>
71
+ API-Key
72
+ <SettingOutlined />
73
+ </Button>
74
+ <Button>
75
+ Show more models
76
+ <SettingOutlined />
77
+ </Button>
78
+ </Space>
79
+ </Col>
80
+ </Row>
81
+ <List
82
+ size="small"
83
+ dataSource={item.llm}
84
+ renderItem={(item) => <List.Item>{item.name}</List.Item>}
85
+ />
86
+ </Card>
87
+ </List.Item>
88
+ )}
89
+ />
90
+ <p>Models to be added</p>
91
+ <List
92
+ grid={{
93
+ gutter: 16,
94
+ xs: 1,
95
+ sm: 2,
96
+ md: 3,
97
+ lg: 4,
98
+ xl: 4,
99
+ xxl: 8,
100
+ }}
101
+ dataSource={factoryList}
102
+ renderItem={(item) => (
103
+ <List.Item>
104
+ <Card>
105
+ <Flex vertical gap={'large'}>
106
+ <Avatar shape="square" size="large" src={item.logo} />
107
+ <Flex vertical gap={'middle'}>
108
+ <b>{item.name}</b>
109
+ <Space wrap>
110
+ {item.tags.split(',').map((x) => (
111
+ <Tag key={x}>{x}</Tag>
112
+ ))}
113
+ </Space>
114
+ </Flex>
115
+ </Flex>
116
+ </Card>
117
+ </List.Item>
118
+ )}
119
+ />
120
+ </section>
121
+ <ApiKeyModal
122
+ visible={apiKeyVisible}
123
+ hideModal={hideApiKeyModal}
124
+ loading={saveApiKeyLoading}
125
+ initialValue={initialApiKey}
126
+ onOk={onApiKeySavingOk}
127
+ ></ApiKeyModal>
128
+ </>
129
+ );
130
  };
131
 
132
  export default UserSettingModel;