diff --git a/web/src/assets/svg/more-model.svg b/web/src/assets/svg/more-model.svg new file mode 100644 index 0000000000000000000000000000000000000000..c54f885c0d6b025dc80f69164fd98f19f72cab9b --- /dev/null +++ b/web/src/assets/svg/more-model.svg @@ -0,0 +1,14 @@ +<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path + d="M17.6667 18.3337C17.9 18.3337 18.0167 18.3337 18.1059 18.2882C18.1843 18.2483 18.248 18.1846 18.2879 18.1062C18.3334 18.017 18.3334 17.9003 18.3334 17.667V9.00033C18.3334 8.76697 18.3334 8.65029 18.2879 8.56116C18.248 8.48276 18.1843 8.41902 18.1059 8.37907C18.0167 8.33366 17.9 8.33366 17.6667 8.33366L15.6667 8.33366C15.4333 8.33366 15.3167 8.33366 15.2275 8.37907C15.1491 8.41902 15.0854 8.48276 15.0454 8.56116C15 8.65029 15 8.76697 15 9.00033V11.0003C15 11.2337 15 11.3504 14.9546 11.4395C14.9147 11.5179 14.8509 11.5816 14.7725 11.6216C14.6834 11.667 14.5667 11.667 14.3334 11.667H12.3334C12.1 11.667 11.9833 11.667 11.8942 11.7124C11.8158 11.7524 11.752 11.8161 11.7121 11.8945C11.6667 11.9836 11.6667 12.1003 11.6667 12.3337V14.3337C11.6667 14.567 11.6667 14.6837 11.6213 14.7728C11.5813 14.8512 11.5176 14.915 11.4392 14.9549C11.3501 15.0003 11.2334 15.0003 11 15.0003H9.00002C8.76667 15.0003 8.64999 15.0003 8.56086 15.0457C8.48246 15.0857 8.41872 15.1494 8.37877 15.2278C8.33335 15.317 8.33335 15.4336 8.33335 15.667V17.667C8.33335 17.9003 8.33335 18.017 8.37877 18.1062C8.41872 18.1846 8.48246 18.2483 8.56086 18.2882C8.64999 18.3337 8.76667 18.3337 9.00002 18.3337L17.6667 18.3337Z" + stroke="#98A2B3" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" /> + <path + d="M8.33335 5.66699C8.33335 5.43364 8.33335 5.31696 8.37877 5.22783C8.41872 5.14943 8.48246 5.08569 8.56086 5.04574C8.64999 5.00033 8.76667 5.00033 9.00002 5.00033H11C11.2334 5.00033 11.3501 5.00033 11.4392 5.04574C11.5176 5.08569 11.5813 5.14943 11.6213 5.22783C11.6667 5.31696 11.6667 5.43364 11.6667 5.66699V7.66699C11.6667 7.90035 11.6667 8.01703 11.6213 8.10616C11.5813 8.18456 11.5176 8.2483 11.4392 8.28825C11.3501 8.33366 11.2334 8.33366 11 8.33366H9.00002C8.76667 8.33366 8.64999 8.33366 8.56086 8.28825C8.48246 8.2483 8.41872 8.18456 8.37877 8.10616C8.33335 8.01703 8.33335 7.90035 8.33335 7.66699V5.66699Z" + stroke="#98A2B3" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" /> + <path + d="M2.50002 10.667C2.50002 10.4336 2.50002 10.317 2.54543 10.2278C2.58538 10.1494 2.64912 10.0857 2.72752 10.0457C2.81665 10.0003 2.93333 10.0003 3.16669 10.0003H5.16669C5.40004 10.0003 5.51672 10.0003 5.60585 10.0457C5.68425 10.0857 5.74799 10.1494 5.78794 10.2278C5.83335 10.317 5.83335 10.4336 5.83335 10.667V12.667C5.83335 12.9003 5.83335 13.017 5.78794 13.1062C5.74799 13.1846 5.68425 13.2483 5.60585 13.2882C5.51672 13.3337 5.40004 13.3337 5.16669 13.3337H3.16669C2.93333 13.3337 2.81665 13.3337 2.72752 13.2882C2.64912 13.2483 2.58538 13.1846 2.54543 13.1062C2.50002 13.017 2.50002 12.9003 2.50002 12.667V10.667Z" + stroke="#98A2B3" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" /> + <path + d="M1.66669 2.33366C1.66669 2.1003 1.66669 1.98363 1.7121 1.8945C1.75205 1.8161 1.81579 1.75235 1.89419 1.71241C1.98332 1.66699 2.1 1.66699 2.33335 1.66699H4.33335C4.56671 1.66699 4.68339 1.66699 4.77252 1.71241C4.85092 1.75235 4.91466 1.8161 4.95461 1.8945C5.00002 1.98363 5.00002 2.1003 5.00002 2.33366V4.33366C5.00002 4.56701 5.00002 4.68369 4.95461 4.77282C4.91466 4.85122 4.85092 4.91496 4.77252 4.95491C4.68339 5.00033 4.56671 5.00033 4.33335 5.00033H2.33335C2.1 5.00033 1.98332 5.00033 1.89419 4.95491C1.81579 4.91496 1.75205 4.85122 1.7121 4.77282C1.66669 4.68369 1.66669 4.56701 1.66669 4.33366V2.33366Z" + stroke="#98A2B3" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" /> +</svg> \ No newline at end of file diff --git a/web/src/hooks/commonHooks.ts b/web/src/hooks/commonHooks.ts index 6cbf10e8974d50eda4daa762cfd306565bcd7a95..21e6641e12a6150a816880f04ffedddf5e2400ac 100644 --- a/web/src/hooks/commonHooks.ts +++ b/web/src/hooks/commonHooks.ts @@ -11,7 +11,11 @@ export const useSetModalState = () => { setVisible(false); }; - return { visible, showModal, hideModal }; + const switchVisible = () => { + setVisible(!visible); + }; + + return { visible, showModal, hideModal, switchVisible }; }; export const useDeepCompareEffect = ( diff --git a/web/src/hooks/llmHooks.ts b/web/src/hooks/llmHooks.ts index eed420c8ead968f2a70a7ff9ce82292fd0f9556e..74363b21a57d113171d54ee9922b6583f744d097 100644 --- a/web/src/hooks/llmHooks.ts +++ b/web/src/hooks/llmHooks.ts @@ -7,7 +7,7 @@ import { import { useCallback, useEffect, useMemo } from 'react'; import { useDispatch, useSelector } from 'umi'; -export const useFetchLlmList = (modelType: LlmModelType) => { +export const useFetchLlmList = (modelType?: LlmModelType) => { const dispatch = useDispatch(); const fetchLlmList = useCallback(() => { @@ -85,19 +85,20 @@ export const useFetchLlmFactoryListOnMount = () => { return list; }; +export type LlmItem = { name: string; logo: string } & IMyLlmValue; + export const useFetchMyLlmListOnMount = () => { const dispatch = useDispatch(); const llmList = useSelectMyLlmList(); const factoryList = useSelectLlmFactoryList(); - const list: Array<{ name: string; logo: string } & IMyLlmValue> = - useMemo(() => { - return Object.entries(llmList).map(([key, value]) => ({ - name: key, - logo: factoryList.find((x) => x.name === key)?.logo ?? '', - ...value, - })); - }, [llmList, factoryList]); + const list: Array<LlmItem> = useMemo(() => { + return Object.entries(llmList).map(([key, value]) => ({ + name: key, + logo: factoryList.find((x) => x.name === key)?.logo ?? '', + ...value, + })); + }, [llmList, factoryList]); const fetchMyLlmList = useCallback(() => { dispatch({ @@ -135,3 +136,28 @@ export const useSaveApiKey = () => { return saveApiKey; }; + +export interface ISystemModelSettingSavingParams { + tenant_id: string; + name?: string; + asr_id: string; + embd_id: string; + img2txt_id: string; + llm_id: string; +} + +export const useSaveTenantInfo = () => { + const dispatch = useDispatch(); + + const saveTenantInfo = useCallback( + (savingParams: ISystemModelSettingSavingParams) => { + return dispatch<any>({ + type: 'settingModel/set_tenant_info', + payload: savingParams, + }); + }, + [dispatch], + ); + + return saveTenantInfo; +}; diff --git a/web/src/pages/setting/model.ts b/web/src/pages/setting/model.ts index 79f1e92bc11cf4e731dbac9f9962d4dfdf35c122..3157f896b3a44950763e8a4aa6b06e5930ec5ce5 100644 --- a/web/src/pages/setting/model.ts +++ b/web/src/pages/setting/model.ts @@ -142,7 +142,7 @@ const model: DvaModel<SettingModelState> = { } }, *my_llm({ payload = {} }, { call, put }) { - const { data } = yield call(userService.my_llm, payload); + const { data } = yield call(userService.my_llm); const { retcode, data: res } = data; if (retcode === 0) { yield put({ @@ -158,6 +158,8 @@ const model: DvaModel<SettingModelState> = { const { retcode } = data; if (retcode === 0) { message.success('Modified!'); + yield put({ type: 'my_llm' }); + yield put({ type: 'factories_list' }); yield put({ type: 'updateState', }); diff --git a/web/src/pages/user-setting/components/setting-title/index.tsx b/web/src/pages/user-setting/components/setting-title/index.tsx index 1996d50773bc50d7c0f4b52dbba1d73affb89d7f..8ad153f3ad9e12f1c75ab7d77f0b157f42a29960 100644 --- a/web/src/pages/user-setting/components/setting-title/index.tsx +++ b/web/src/pages/user-setting/components/setting-title/index.tsx @@ -1,18 +1,33 @@ -import { Typography } from 'antd'; +import { SettingOutlined } from '@ant-design/icons'; +import { Button, Flex, Typography } from 'antd'; const { Title, Paragraph } = Typography; interface IProps { title: string; description: string; + showRightButton?: boolean; + clickButton?: () => void; } -const SettingTitle = ({ title, description }: IProps) => { +const SettingTitle = ({ + title, + description, + clickButton, + showRightButton = false, +}: IProps) => { return ( - <div> - <Title level={5}>{title}</Title> - <Paragraph>{description}</Paragraph> - </div> + <Flex align="center" justify={'space-between'}> + <div> + <Title level={5}>{title}</Title> + <Paragraph>{description}</Paragraph> + </div> + {showRightButton && ( + <Button type={'primary'} onClick={clickButton}> + <SettingOutlined></SettingOutlined> System Model Settings + </Button> + )} + </Flex> ); }; diff --git a/web/src/pages/user-setting/setting-model/api-key-modal/index.tsx b/web/src/pages/user-setting/setting-model/api-key-modal/index.tsx index 62b403ab937f2fdf7630459b54e0af97f5c3d8bd..3829cef99222d21b50def8a3f8160ba1940334ec 100644 --- a/web/src/pages/user-setting/setting-model/api-key-modal/index.tsx +++ b/web/src/pages/user-setting/setting-model/api-key-modal/index.tsx @@ -64,7 +64,7 @@ const ApiKeyModal = ({ form={form} > <Form.Item<FieldType> - label="Api key" + label="Api-Key" name="api_key" rules={[{ required: true, message: 'Please input api key!' }]} > diff --git a/web/src/pages/user-setting/setting-model/hooks.ts b/web/src/pages/user-setting/setting-model/hooks.ts index 725f6328dd04b7ac9f320937d344403a56407138..b0e70f2907acb55072b088125854d185648ddd3e 100644 --- a/web/src/pages/user-setting/setting-model/hooks.ts +++ b/web/src/pages/user-setting/setting-model/hooks.ts @@ -1,6 +1,16 @@ import { useSetModalState } from '@/hooks/commonHooks'; -import { IApiKeySavingParams, useSaveApiKey } from '@/hooks/llmHooks'; +import { + IApiKeySavingParams, + ISystemModelSettingSavingParams, + useFetchLlmList, + useSaveApiKey, + useSaveTenantInfo, +} from '@/hooks/llmHooks'; import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks'; +import { + useFetchTenantInfo, + useSelectTenantInfo, +} from '@/hooks/userSettingHook'; import { useCallback, useState } from 'react'; type SavingParamsState = Omit<IApiKeySavingParams, 'api_key'>; @@ -20,7 +30,7 @@ export const useSubmitApiKey = () => { async (apiKey: string) => { const ret = await saveApiKey({ ...savingParams, api_key: apiKey }); - if (ret.retcode === 0) { + if (ret === 0) { hideApiKeyModal(); } }, @@ -48,3 +58,49 @@ export const useSubmitApiKey = () => { showApiKeyModal: onShowApiKeyModal, }; }; + +export const useSubmitSystemModelSetting = () => { + const systemSetting = useSelectTenantInfo(); + const loading = useOneNamespaceEffectsLoading('settingModel', [ + 'set_tenant_info', + ]); + const saveSystemModelSetting = useSaveTenantInfo(); + const { + visible: systemSettingVisible, + hideModal: hideSystemSettingModal, + showModal: showSystemSettingModal, + } = useSetModalState(); + + const onSystemSettingSavingOk = useCallback( + async ( + payload: Omit<ISystemModelSettingSavingParams, 'tenant_id' | 'name'>, + ) => { + const ret = await saveSystemModelSetting({ + tenant_id: systemSetting.tenant_id, + name: systemSetting.name, + ...payload, + }); + + if (ret === 0) { + hideSystemSettingModal(); + } + }, + [hideSystemSettingModal, saveSystemModelSetting, systemSetting], + ); + + return { + saveSystemModelSettingLoading: loading, + onSystemSettingSavingOk, + systemSettingVisible, + hideSystemSettingModal, + showSystemSettingModal, + }; +}; + +export const useFetchSystemModelSettingOnMount = () => { + const systemSetting = useSelectTenantInfo(); + useFetchLlmList(); + useFetchTenantInfo(); + + return systemSetting; +}; diff --git a/web/src/pages/user-setting/setting-model/index.less b/web/src/pages/user-setting/setting-model/index.less index d91b0c01d91853ebbc7969144957fb613df89b5f..128c5df85ff44e6534519678daee0ae141bb1234 100644 --- a/web/src/pages/user-setting/setting-model/index.less +++ b/web/src/pages/user-setting/setting-model/index.less @@ -3,4 +3,21 @@ .factoryOperationWrapper { text-align: right; } + .modelItem { + } + .llmList { + padding-top: 10px; + } + .toBeAddedCard { + border-radius: 24px; + border: 1px solid #eaecf0; + background: #e3f0ff; + box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05); + } + .addedCard { + border-radius: 18px; + border: 1px solid #eaecf0; + background: #e6e7eb; + box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05); + } } diff --git a/web/src/pages/user-setting/setting-model/index.tsx b/web/src/pages/user-setting/setting-model/index.tsx index 451d2334106e2229e9bc77eae0a5fa3bdfda30b9..8ce57d9eda4881a5c6cc11e01771b829bb89b5e6 100644 --- a/web/src/pages/user-setting/setting-model/index.tsx +++ b/web/src/pages/user-setting/setting-model/index.tsx @@ -1,4 +1,7 @@ +import { ReactComponent as MoreModelIcon } from '@/assets/svg/more-model.svg'; +import { useSetModalState } from '@/hooks/commonHooks'; import { + LlmItem, useFetchLlmFactoryListOnMount, useFetchMyLlmListOnMount, } from '@/hooks/llmHooks'; @@ -13,14 +16,74 @@ import { List, Row, Space, - Tag, + Typography, } from 'antd'; +import { useCallback } from 'react'; import SettingTitle from '../components/setting-title'; import ApiKeyModal from './api-key-modal'; -import { useSubmitApiKey } from './hooks'; +import { useSubmitApiKey, useSubmitSystemModelSetting } from './hooks'; +import SystemModelSettingModal from './system-model-setting-modal'; import styles from './index.less'; +const { Text } = Typography; +interface IModelCardProps { + item: LlmItem; + clickApiKey: (llmFactory: string) => void; +} + +const ModelCard = ({ item, clickApiKey }: IModelCardProps) => { + const { visible, switchVisible } = useSetModalState(); + + const handleApiKeyClick = () => { + clickApiKey(item.name); + }; + + const handleShowMoreClick = () => { + switchVisible(); + }; + + return ( + <List.Item> + <Card className={styles.addedCard}> + <Row align={'middle'}> + <Col span={12}> + <Flex gap={'middle'} align="center"> + <Avatar shape="square" size="large" src={item.logo} /> + <Flex vertical gap={'small'}> + <b>{item.name}</b> + <Text>{item.tags}</Text> + </Flex> + </Flex> + </Col> + <Col span={12} className={styles.factoryOperationWrapper}> + <Space size={'middle'}> + <Button onClick={handleApiKeyClick}> + API-Key + <SettingOutlined /> + </Button> + <Button onClick={handleShowMoreClick}> + <Flex gap={'small'}> + Show more models + <MoreModelIcon /> + </Flex> + </Button> + </Space> + </Col> + </Row> + {visible && ( + <List + size="small" + dataSource={item.llm} + className={styles.llmList} + renderItem={(item) => <List.Item>{item.name}</List.Item>} + /> + )} + </Card> + </List.Item> + ); +}; + const UserSettingModel = () => { const factoryList = useFetchLlmFactoryListOnMount(); const llmList = useFetchMyLlmListOnMount(); @@ -32,9 +95,23 @@ const UserSettingModel = () => { hideApiKeyModal, showApiKeyModal, } = useSubmitApiKey(); + const { + saveSystemModelSettingLoading, + onSystemSettingSavingOk, + systemSettingVisible, + hideSystemSettingModal, + showSystemSettingModal, + } = useSubmitSystemModelSetting(); + + const handleApiKeyClick = useCallback( + (llmFactory: string) => { + showApiKeyModal({ llm_factory: llmFactory }); + }, + [showApiKeyModal], + ); - const handleApiKeyClick = (llmFactory: string) => () => { - showApiKeyModal({ llm_factory: llmFactory }); + const handleAddModel = (llmFactory: string) => () => { + handleApiKeyClick(llmFactory); }; return ( @@ -43,48 +120,15 @@ const UserSettingModel = () => { <SettingTitle title="Model Setting" description="Manage your account settings and preferences here." + showRightButton + clickButton={showSystemSettingModal} ></SettingTitle> <Divider></Divider> <List grid={{ gutter: 16, column: 1 }} dataSource={llmList} renderItem={(item) => ( - <List.Item> - <Card> - <Row align={'middle'}> - <Col span={12}> - <Flex gap={'middle'} align="center"> - <Avatar shape="square" size="large" src={item.logo} /> - <Flex vertical gap={'small'}> - <b>{item.name}</b> - <div> - {item.tags.split(',').map((x) => ( - <Tag key={x}>{x}</Tag> - ))} - </div> - </Flex> - </Flex> - </Col> - <Col span={12} className={styles.factoryOperationWrapper}> - <Space size={'middle'}> - <Button onClick={handleApiKeyClick(item.name)}> - API-Key - <SettingOutlined /> - </Button> - <Button> - Show more models - <SettingOutlined /> - </Button> - </Space> - </Col> - </Row> - <List - size="small" - dataSource={item.llm} - renderItem={(item) => <List.Item>{item.name}</List.Item>} - /> - </Card> - </List.Item> + <ModelCard item={item} clickApiKey={handleApiKeyClick}></ModelCard> )} /> <p>Models to be added</p> @@ -101,18 +145,18 @@ const UserSettingModel = () => { dataSource={factoryList} renderItem={(item) => ( <List.Item> - <Card> + <Card className={styles.toBeAddedCard}> <Flex vertical gap={'large'}> <Avatar shape="square" size="large" src={item.logo} /> <Flex vertical gap={'middle'}> <b>{item.name}</b> - <Space wrap> - {item.tags.split(',').map((x) => ( - <Tag key={x}>{x}</Tag> - ))} - </Space> + <Text>{item.tags}</Text> </Flex> </Flex> + <Divider></Divider> + <Button type="link" onClick={handleAddModel(item.name)}> + Add the model + </Button> </Card> </List.Item> )} @@ -125,6 +169,12 @@ const UserSettingModel = () => { initialValue={initialApiKey} onOk={onApiKeySavingOk} ></ApiKeyModal> + <SystemModelSettingModal + visible={systemSettingVisible} + onOk={onSystemSettingSavingOk} + hideModal={hideSystemSettingModal} + loading={saveSystemModelSettingLoading} + ></SystemModelSettingModal> </> ); }; diff --git a/web/src/pages/user-setting/setting-model/system-model-setting-modal/index.tsx b/web/src/pages/user-setting/setting-model/system-model-setting-modal/index.tsx new file mode 100644 index 0000000000000000000000000000000000000000..1edd60479da9da84bb5023c4396e4f2b1f9fa338 --- /dev/null +++ b/web/src/pages/user-setting/setting-model/system-model-setting-modal/index.tsx @@ -0,0 +1,61 @@ +import { IModalManagerChildrenProps } from '@/components/modal-manager'; +import { ISystemModelSettingSavingParams } from '@/hooks/llmHooks'; +import { Form, Modal, Select } from 'antd'; +import { useEffect } from 'react'; +import { useFetchSystemModelSettingOnMount } from '../hooks'; + +interface IProps extends Omit<IModalManagerChildrenProps, 'showModal'> { + loading: boolean; + onOk: ( + payload: Omit<ISystemModelSettingSavingParams, 'tenant_id' | 'name'>, + ) => void; +} + +const SystemModelSettingModal = ({ + visible, + hideModal, + onOk, + loading, +}: IProps) => { + const [form] = Form.useForm(); + const initialValues = useFetchSystemModelSettingOnMount(); + + const handleOk = async () => { + const values = await form.validateFields(); + onOk(values); + }; + + useEffect(() => { + form.setFieldsValue(initialValues); + }, [form, initialValues]); + + const onFormLayoutChange = () => {}; + + return ( + <Modal + title="Basic Modal" + open={visible} + onOk={handleOk} + onCancel={hideModal} + okButtonProps={{ loading }} + confirmLoading={loading} + > + <Form form={form} onValuesChange={onFormLayoutChange} layout={'vertical'}> + <Form.Item label="sequence2txt model" name="asr_id"> + <Select options={[{ value: 'sample', label: <span>sample</span> }]} /> + </Form.Item> + <Form.Item label="Embedding model" name="embd_id"> + <Select options={[{ value: 'sample', label: <span>sample</span> }]} /> + </Form.Item> + <Form.Item label="img2txt_id model" name="img2txt_id"> + <Select options={[{ value: 'sample', label: <span>sample</span> }]} /> + </Form.Item> + <Form.Item label="Chat model" name="llm_id"> + <Select options={[{ value: 'sample', label: <span>sample</span> }]} /> + </Form.Item> + </Form> + </Modal> + ); +}; + +export default SystemModelSettingModal;