From 46ecc2467bab29d07695d87fbfa3ee34d7d54d5d Mon Sep 17 00:00:00 2001 From: jialin Date: Mon, 29 Sep 2025 14:49:49 +0800 Subject: [PATCH] refactor: advance collapse --- config/config.ts | 10 +- .../_components/column-wrapper/index.tsx | 20 +- src/pages/backends/components/add-modal.tsx | 6 +- .../llmodels/components/advance-config.tsx | 84 +------ src/pages/llmodels/components/data-form.tsx | 44 +++- .../llmodels/components/deploy-dropdown.tsx | 32 --- .../llmodels/components/deploy-modal.tsx | 41 ++-- .../llmodels/components/search-model.tsx | 28 +-- .../llmodels/components/search-result.tsx | 5 +- src/pages/llmodels/config/form-context.ts | 2 +- src/pages/llmodels/forms/advance-config.tsx | 211 ++++++++++++++++++ .../forms/backend-parameters-list.tsx | 93 ++++++++ .../forms/{backend-fields.tsx => backend.tsx} | 0 .../{local-path.tsx => local-path-source.tsx} | 12 +- .../{hugging-face.tsx => online-source.tsx} | 0 .../{components => forms}/performance.tsx | 2 +- src/pages/llmodels/style/model-card.less | 5 - src/pages/llmodels/style/search-result.less | 5 +- 18 files changed, 423 insertions(+), 177 deletions(-) delete mode 100644 src/pages/llmodels/components/deploy-dropdown.tsx create mode 100644 src/pages/llmodels/forms/advance-config.tsx create mode 100644 src/pages/llmodels/forms/backend-parameters-list.tsx rename src/pages/llmodels/forms/{backend-fields.tsx => backend.tsx} (100%) rename src/pages/llmodels/forms/{local-path.tsx => local-path-source.tsx} (91%) rename src/pages/llmodels/forms/{hugging-face.tsx => online-source.tsx} (100%) rename src/pages/llmodels/{components => forms}/performance.tsx (99%) diff --git a/config/config.ts b/config/config.ts index 8c16aba8..7ed4d0f5 100644 --- a/config/config.ts +++ b/config/config.ts @@ -58,12 +58,14 @@ export default defineConfig({ .filename(`js/[name].${t}.js`) .chunkFilename(`js/[name].${t}.chunk.js`); compressionPluginConfig(config); + monacoPluginConfig(config); } } - : {}), - chainWebpack(config) { - monacoPluginConfig(config); - }, + : { + chainWebpack(config) { + monacoPluginConfig(config); + } + }), favicons: ['/static/favicon.png'], jsMinifier: 'terser', cssMinifier: 'cssnano', diff --git a/src/pages/_components/column-wrapper/index.tsx b/src/pages/_components/column-wrapper/index.tsx index 8cef3911..71e3b14f 100644 --- a/src/pages/_components/column-wrapper/index.tsx +++ b/src/pages/_components/column-wrapper/index.tsx @@ -2,11 +2,23 @@ import useOverlayScroller from '@/hooks/use-overlay-scroller'; import React from 'react'; import './style.less'; -const ColumnWrapper: React.FC = ({ +interface ColumnWrapperProps { + children: React.ReactNode; + footer?: React.ReactNode; + maxHeight?: string | number; + paddingBottom?: number; + styles?: { + wrapper?: React.CSSProperties; + container?: React.CSSProperties; + }; +} + +const ColumnWrapper: React.FC = ({ children, footer, maxHeight, - paddingBottom = 50 + paddingBottom = 50, + styles = {} }) => { const scroller = React.useRef(null); const { initialize } = useOverlayScroller({ @@ -27,12 +39,12 @@ const ColumnWrapper: React.FC = ({ <>
= (props) => { const { action, currentData, onClose, onSubmit, onSubmitYaml, open, title } = props; + const uid = useId(); const formRef = useRef(null); const editorRef = useRef(null); const intl = useIntl(); @@ -121,7 +122,7 @@ const AddModal: React.FC = (props) => { setYamlContent(yaml); setFormContent(formData); formRef.current?.setFieldsValue?.(formData); - editorRef.current?.setValue?.(yaml); + editorRef.current?.setContent?.(yaml); } }, [action, currentData]); @@ -129,6 +130,7 @@ const AddModal: React.FC = (props) => { = (props) => { - const { form, isGGUF, gpuOptions, source, action } = props; +const AdvanceConfig = () => { const intl = useIntl(); + const form = Form.useFormInstance(); const wokerSelector = Form.useWatch('worker_selector', form); const EnviromentVars = Form.useWatch('env', form); const scheduleType = Form.useWatch('scheduleType', form); @@ -57,7 +56,7 @@ const AdvanceConfig: React.FC = (props) => { const placement_strategy = Form.useWatch('placement_strategy', form); const gpuSelectorIds = Form.useWatch(['gpu_selector', 'gpu_ids'], form); const worker_selector = Form.useWatch('worker_selector', form); - const { onValuesChange } = useFormContext(); + const { onValuesChange, gpuOptions, source, isGGUF } = useFormContext(); const paramsConfig = useMemo(() => { return _.get(BackendParameters, backend, []); @@ -131,7 +130,7 @@ const AdvanceConfig: React.FC = (props) => { options={modelCategories} > - + {scheduleType === ScheduleValueMap.Auto && ( <> name="placement_strategy"> @@ -194,57 +193,7 @@ const AdvanceConfig: React.FC = (props) => { )} - name="backend_parameters"> - - {backend === backendOptionsMap.ascendMindie && ( - - {intl.formatMessage({ id: 'models.backend.mindie.310p' })} - - )} - - {intl.formatMessage( - { id: 'models.form.backend_parameters.vllm.tips' }, - { backend: backendParamsTips.backend || '' } - )}{' '} - - - {intl.formatMessage({ id: 'common.text.here' })} - - - - - - ) - } - > - + name="env"> = (props) => { forceRender: true, children: }, - // { - // key: '3', - // label: ( - // - // Scaling - // - // ), - // forceRender: true, - // children: - // }, { key: '1', label: ( diff --git a/src/pages/llmodels/components/data-form.tsx b/src/pages/llmodels/components/data-form.tsx index d81cad9f..bc0c0d06 100644 --- a/src/pages/llmodels/components/data-form.tsx +++ b/src/pages/llmodels/components/data-form.tsx @@ -2,6 +2,7 @@ import SealInput from '@/components/seal-form/seal-input'; import SealSelect from '@/components/seal-form/seal-select'; import { PageActionType } from '@/config/types'; import useAppUtils from '@/hooks/use-app-utils'; +import CollapsePanel from '@/pages/_components/collapse-panel'; import { useIntl } from '@umijs/max'; import { Form } from 'antd'; import _ from 'lodash'; @@ -17,10 +18,12 @@ import { } from '../config/types'; import { generateGPUIds } from '../config/utils'; import CatalogFrom from '../forms/catalog'; -import HuggingFaceForm from '../forms/hugging-face'; -import LocalPathForm from '../forms/local-path'; +import LocalPathSource from '../forms/local-path-source'; +import OnlineSource from '../forms/online-source'; import { useGenerateGPUOptions } from '../hooks/use-form-initial-values'; -import AdvanceConfig from './advance-config'; +// import AdvanceConfig from './advance-config'; +import AdvanceConfig from '../forms/advance-config'; +import Performance from '../forms/performance'; interface DataFormProps { initialValues?: any; @@ -59,6 +62,7 @@ const DataForm: React.FC = forwardRef((props, ref) => { const { getRuleMessage } = useAppUtils(); const [form] = Form.useForm(); const intl = useIntl(); + const [activeKey, setActiveKey] = React.useState([]); const handleSumit = () => { form.submit(); @@ -118,6 +122,10 @@ const DataForm: React.FC = forwardRef((props, ref) => { onValuesChange?.(changedValues, allValues); }; + const handleOnCollapseChange = (keys: string | string[]) => { + setActiveKey(Array.isArray(keys) ? keys : [keys]); + }; + useImperativeHandle(ref, () => { return { form: form, @@ -216,8 +224,8 @@ const DataForm: React.FC = forwardRef((props, ref) => { )} - - + + name="cluster_id" rules={[ @@ -245,13 +253,25 @@ const DataForm: React.FC = forwardRef((props, ref) => { })} > - + + }, + { + key: 'advance_config', + label: 'Advanced', + forceRender: true, + children: + } + ]} + > ); diff --git a/src/pages/llmodels/components/deploy-dropdown.tsx b/src/pages/llmodels/components/deploy-dropdown.tsx deleted file mode 100644 index a9259fd2..00000000 --- a/src/pages/llmodels/components/deploy-dropdown.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { Col, Row } from 'antd'; -import React from 'react'; -import '../style/deploy-dropdown.less'; - -interface DeployDropdownProps { - items: { label: string; value: string; key: string; icon: React.ReactNode }[]; - onSelect: (item: { - label: string; - value: string; - key: string; - icon: React.ReactNode; - }) => void; -} - -const DeployDropdown: React.FC = ({ items, onSelect }) => { - return ( -
- - {items.map((item) => ( - -
onSelect(item)} className="item"> - {item.icon} - {item.label} -
- - ))} -
-
- ); -}; - -export default DeployDropdown; diff --git a/src/pages/llmodels/components/deploy-modal.tsx b/src/pages/llmodels/components/deploy-modal.tsx index b185a234..8e72fa0d 100644 --- a/src/pages/llmodels/components/deploy-modal.tsx +++ b/src/pages/llmodels/components/deploy-modal.tsx @@ -36,10 +36,15 @@ const ModalFooterStyle = { justifyContent: 'flex-end' }; +const Container = styled.div` + display: flex; + height: 100%; +`; + const ColWrapper = styled.div` display: flex; flex: 1; - maxwidth: 33.33%; + max-width: 33.33%; `; const FormWrapper = styled.div` @@ -445,31 +450,27 @@ const AddModal: FC = (props) => { width={width} footer={false} > -
+ {SEARCH_SOURCE.includes(props.source) && deploymentType === 'modelList' && ( <> - - - + - + = (props) => { -
+
); }; diff --git a/src/pages/llmodels/components/search-model.tsx b/src/pages/llmodels/components/search-model.tsx index 36044630..58c91e2b 100644 --- a/src/pages/llmodels/components/search-model.tsx +++ b/src/pages/llmodels/components/search-model.tsx @@ -1,6 +1,7 @@ import { getRequestId, setRquestId } from '@/atoms/models'; import BaseSelect from '@/components/seal-form/base/select'; import { createAxiosToken } from '@/hooks/use-chunk-request'; +import ColumnWrapper from '@/pages/_components/column-wrapper'; import { useIntl } from '@umijs/max'; import { Pagination } from 'antd'; import _ from 'lodash'; @@ -521,11 +522,11 @@ const SearchModel: React.FC = (props) => { onChange={handleSearchInputChange} modelSource={modelSource} > -
+ {/*
{intl.formatMessage({ id: 'models.form.search.gguftips' })} -
+
*/}
= (props) => { }, []); return ( -
+
{renderHFSearch()}
- - + + +
); }; diff --git a/src/pages/llmodels/components/search-result.tsx b/src/pages/llmodels/components/search-result.tsx index c1190dee..181a9aa3 100644 --- a/src/pages/llmodels/components/search-result.tsx +++ b/src/pages/llmodels/components/search-result.tsx @@ -1,5 +1,4 @@ import IconFont from '@/components/icon-font'; -import SimpleOverlay from '@/components/simple-overlay'; import { SearchOutlined } from '@ant-design/icons'; import { useIntl } from '@umijs/max'; import { Button, Empty, Spin } from 'antd'; @@ -121,7 +120,7 @@ const SearchResult: React.FC = (props) => { }, [networkError, source, intl]); return ( - + <>
@@ -167,7 +166,7 @@ const SearchResult: React.FC = (props) => {
-
+ ); }; diff --git a/src/pages/llmodels/config/form-context.ts b/src/pages/llmodels/config/form-context.ts index 19fab29c..9ba9e7e8 100644 --- a/src/pages/llmodels/config/form-context.ts +++ b/src/pages/llmodels/config/form-context.ts @@ -7,7 +7,7 @@ interface FormContextProps { formKey: DeployFormKey; source: string; pageAction: PageActionType; - gpuOptions?: any[]; + gpuOptions: any[]; backendOptions: BackendOption[]; onValuesChange?: (changedValues: any, allValues: any) => void; onBackendChange: (backend: string, option: any) => void; diff --git a/src/pages/llmodels/forms/advance-config.tsx b/src/pages/llmodels/forms/advance-config.tsx new file mode 100644 index 00000000..f5082b3d --- /dev/null +++ b/src/pages/llmodels/forms/advance-config.tsx @@ -0,0 +1,211 @@ +import LabelSelector from '@/components/label-selector'; +import CheckboxField from '@/components/seal-form/checkbox-field'; +import SealSelect from '@/components/seal-form/seal-select'; +import TooltipList from '@/components/tooltip-list'; +import { useIntl } from '@umijs/max'; +import { Form } from 'antd'; +import _ from 'lodash'; +import { useCallback } from 'react'; +import { + modelCategories, + placementStrategyOptions, + ScheduleValueMap +} from '../config'; +import { backendOptionsMap } from '../config/backend-parameters'; +import { useFormContext } from '../config/form-context'; +import { FormData } from '../config/types'; +import Backend from '../forms/backend'; +import BackendParametersList from '../forms/backend-parameters-list'; + +const placementStrategyTips = [ + { + title: 'Spread', + tips: 'resources.form.spread.tips' + }, + { + title: 'Binpack', + tips: 'resources.form.binpack.tips' + } +]; + +const AdvanceConfig = () => { + const intl = useIntl(); + const form = Form.useFormInstance(); + const wokerSelector = Form.useWatch('worker_selector', form); + const EnviromentVars = Form.useWatch('env', form); + const scheduleType = Form.useWatch('scheduleType', form); + const backend = Form.useWatch('backend', form); + const { onValuesChange } = useFormContext(); + + const handleWorkerLabelsChange = useCallback( + (labels: Record) => { + form.setFieldValue('worker_selector', labels); + }, + [] + ); + const handleEnviromentVarsChange = useCallback( + (labels: Record) => { + form.setFieldValue('env', labels); + }, + [] + ); + + const onSelectorChange = (field: string, allowEmpty?: boolean) => { + const workerSelector = form.getFieldValue(field); + // check if all keys have values + const hasEmptyValue = _.some(_.keys(workerSelector), (k: string) => { + return !workerSelector[k]; + }); + if (!hasEmptyValue || allowEmpty) { + onValuesChange?.({}, form.getFieldsValue()); + } + }; + + const handleSelectorOnBlur = () => { + onSelectorChange('worker_selector'); + }; + + const handleDeleteWorkerSelector = (index: number) => { + onValuesChange?.({}, form.getFieldsValue()); + }; + + const handleEnvSelectorOnBlur = () => { + onSelectorChange('env', true); + }; + + const handleDeleteEnvSelector = (index: number) => { + onValuesChange?.({}, form.getFieldsValue()); + }; + + return ( + <> + name="categories"> + + + + {scheduleType === ScheduleValueMap.Auto && ( + <> + name="placement_strategy"> + + } + > + + + name="worker_selector" + rules={[ + ({ getFieldValue }) => ({ + validator(rule, value) { + if ( + getFieldValue('scheduleType') === ScheduleValueMap.Auto && + _.keys(value).length > 0 + ) { + if (_.some(_.keys(value), (k: string) => !value[k])) { + return Promise.reject( + intl.formatMessage( + { + id: 'common.validate.value' + }, + { + name: intl.formatMessage({ + id: 'models.form.selector' + }) + } + ) + ); + } + } + return Promise.resolve(); + } + }) + ]} + > + + {intl.formatMessage({ + id: 'resources.form.workerSelector.description' + })} + + } + > + + + )} + + + name="env"> + + + + {scheduleType === ScheduleValueMap.Auto && + [backendOptionsMap.vllm, backendOptionsMap.ascendMindie].includes( + backend + ) && ( +
+ + name="distributed_inference_across_workers" + valuePropName="checked" + style={{ padding: '0 10px', marginBottom: 0 }} + noStyle + > + + +
+ )} +
+ + name="restart_on_error" + valuePropName="checked" + style={{ padding: '0 10px', marginBottom: 0 }} + noStyle + > + + +
+ + ); +}; + +export default AdvanceConfig; diff --git a/src/pages/llmodels/forms/backend-parameters-list.tsx b/src/pages/llmodels/forms/backend-parameters-list.tsx new file mode 100644 index 00000000..df051cfb --- /dev/null +++ b/src/pages/llmodels/forms/backend-parameters-list.tsx @@ -0,0 +1,93 @@ +import IconFont from '@/components/icon-font'; +import ListInput from '@/components/list-input'; +import { useIntl } from '@umijs/max'; +import { Form, Typography } from 'antd'; +import _ from 'lodash'; +import { useMemo } from 'react'; +import { backendParamsHolderTips, getBackendParamsTips } from '../config'; +import BackendParameters, { + backendOptionsMap +} from '../config/backend-parameters'; +import { useFormContext } from '../config/form-context'; +import { FormData } from '../config/types'; + +const BackendParametersList: React.FC = () => { + const intl = useIntl(); + const { onValuesChange } = useFormContext(); + const form = Form.useFormInstance(); + const backend = Form.useWatch('backend', form); + + const backendParamsTips = useMemo(() => { + return getBackendParamsTips(backend); + }, [backend]); + + const paramsConfig = useMemo(() => { + return _.get(BackendParameters, backend, []); + }, [backend]); + + const handleBackendParametersChange = (list: string[]) => { + form.setFieldValue('backend_parameters', list); + }; + + const handleBackendParametersOnBlur = () => { + onValuesChange?.({}, form.getFieldsValue()); + }; + + const handleDeleteBackendParameters = (index: number) => { + onValuesChange?.({}, form.getFieldsValue()); + }; + + return ( + name="backend_parameters"> + + {backend === backendOptionsMap.ascendMindie && ( + + {intl.formatMessage({ id: 'models.backend.mindie.310p' })} + + )} + + {intl.formatMessage( + { id: 'models.form.backend_parameters.vllm.tips' }, + { backend: backendParamsTips.backend || '' } + )}{' '} + + {intl.formatMessage({ id: 'common.text.here' })} + + + + + ) + } + > + + ); +}; + +export default BackendParametersList; diff --git a/src/pages/llmodels/forms/backend-fields.tsx b/src/pages/llmodels/forms/backend.tsx similarity index 100% rename from src/pages/llmodels/forms/backend-fields.tsx rename to src/pages/llmodels/forms/backend.tsx diff --git a/src/pages/llmodels/forms/local-path.tsx b/src/pages/llmodels/forms/local-path-source.tsx similarity index 91% rename from src/pages/llmodels/forms/local-path.tsx rename to src/pages/llmodels/forms/local-path-source.tsx index 92b494c1..892c104c 100644 --- a/src/pages/llmodels/forms/local-path.tsx +++ b/src/pages/llmodels/forms/local-path-source.tsx @@ -14,9 +14,14 @@ import useCheckBackend from '../hooks/use-check-backend'; const LocalPathForm: React.FC = () => { const { checkOnlyAscendNPU } = useCheckBackend(); const form = Form.useFormInstance(); - const formCtx = useFormContext(); const source = Form.useWatch('source', form); - const { formKey, gpuOptions, onValuesChange, onBackendChange } = formCtx; + const { + formKey, + gpuOptions, + backendOptions, + onValuesChange, + onBackendChange + } = useFormContext(); const { getRuleMessage } = useAppUtils(); const intl = useIntl(); const localPathCache = useRef(form.getFieldValue('local_path') || ''); @@ -58,7 +63,8 @@ const LocalPathForm: React.FC = () => { }); if (oldBackend !== backend) { - onBackendChange?.(backend); + const option = backendOptions.find((item) => item.value === backend); + onBackendChange?.(backend, option); } else { onValuesChange?.({ local_path: value }, form.getFieldsValue()); } diff --git a/src/pages/llmodels/forms/hugging-face.tsx b/src/pages/llmodels/forms/online-source.tsx similarity index 100% rename from src/pages/llmodels/forms/hugging-face.tsx rename to src/pages/llmodels/forms/online-source.tsx diff --git a/src/pages/llmodels/components/performance.tsx b/src/pages/llmodels/forms/performance.tsx similarity index 99% rename from src/pages/llmodels/components/performance.tsx rename to src/pages/llmodels/forms/performance.tsx index 58a0e973..7f878608 100644 --- a/src/pages/llmodels/components/performance.tsx +++ b/src/pages/llmodels/forms/performance.tsx @@ -5,10 +5,10 @@ import useAppUtils from '@/hooks/use-app-utils'; import { useIntl } from '@umijs/max'; import { Form } from 'antd'; import React from 'react'; +import GPUCard from '../components/gpu-card'; import { scheduleList, ScheduleValueMap } from '../config'; import { backendOptionsMap } from '../config/backend-parameters'; import { useCatalogFormContext, useFormContext } from '../config/form-context'; -import GPUCard from './gpu-card'; const scheduleTypeTips = [ { diff --git a/src/pages/llmodels/style/model-card.less b/src/pages/llmodels/style/model-card.less index d3ce2999..d6650a38 100644 --- a/src/pages/llmodels/style/model-card.less +++ b/src/pages/llmodels/style/model-card.less @@ -26,8 +26,3 @@ justify-content: flex-end; } } - -.card-wrapper { - padding: 16px 24px; - padding-top: 0; -} diff --git a/src/pages/llmodels/style/search-result.less b/src/pages/llmodels/style/search-result.less index f6b645aa..271e37b5 100644 --- a/src/pages/llmodels/style/search-result.less +++ b/src/pages/llmodels/style/search-result.less @@ -1,7 +1,6 @@ @padding: 24px; .search-result-wrap { - padding: 16px @padding; border-radius: 0 0 var(--border-radius-base) var(--border-radius-base); .ant-spin-container.ant-spin-blur { @@ -21,8 +20,6 @@ } .search-bar { - left: 0; - right: 0; padding-inline: @padding; background: var(--ant-color-bg-elevated); padding-bottom: 10px; @@ -32,6 +29,8 @@ justify-content: space-between; align-items: center; margin-top: 10px; + row-gap: 10px; + flex-wrap: wrap; color: var(--ant-color-text-tertiary); :global {