From eb509166ee8d0cd8c23772393035a1c23d7cc249 Mon Sep 17 00:00:00 2001 From: jialin Date: Tue, 30 Sep 2025 12:01:40 +0800 Subject: [PATCH] style: backend list styles --- src/components/seal-form/seal-input.tsx | 1 - src/locales/en-US/clusters.ts | 2 + src/locales/ja-JP/clusters.ts | 7 +- src/locales/ru-RU/clusters.ts | 6 +- src/locales/zh-CN/clusters.ts | 2 + .../backends/components/backend-card.tsx | 43 ++- .../view-builtin-versions-modal.tsx | 76 +++++ src/pages/backends/config/index.ts | 13 + .../backends/forms/built-in-versions.tsx | 4 +- src/pages/backends/forms/versions-config.tsx | 31 +- src/pages/backends/list.tsx | 21 ++ .../components/add-worker-step.tsx | 18 +- .../llmodels/components/advance-config.tsx | 318 ------------------ .../components/deploy-builtin-modal.tsx | 2 +- .../llmodels/components/deploy-modal.tsx | 5 +- .../llmodels/components/update-modal.tsx | 2 +- .../data-form.tsx => forms/index.tsx} | 12 +- src/pages/llmodels/forms/performance.tsx | 6 +- 18 files changed, 209 insertions(+), 360 deletions(-) create mode 100644 src/pages/backends/components/view-builtin-versions-modal.tsx delete mode 100644 src/pages/llmodels/components/advance-config.tsx rename src/pages/llmodels/{components/data-form.tsx => forms/index.tsx} (96%) diff --git a/src/components/seal-form/seal-input.tsx b/src/components/seal-form/seal-input.tsx index 9155fd17..47e7a6fb 100644 --- a/src/components/seal-form/seal-input.tsx +++ b/src/components/seal-form/seal-input.tsx @@ -67,7 +67,6 @@ const SealInput: React.FC = (props) => { if (!inputRef.current?.input?.value) { setIsFocus(false); } - props.onChange?.(e); props.onBlur?.(e); }; diff --git a/src/locales/en-US/clusters.ts b/src/locales/en-US/clusters.ts index a143d9fe..d0942e89 100644 --- a/src/locales/en-US/clusters.ts +++ b/src/locales/en-US/clusters.ts @@ -42,6 +42,8 @@ export default { ' Please make sure the prerequisites for {label} are met before executing the following command.', 'clusters.create.addCommand.tips': ' On the Worker that needs to be added, run the following command to join it to the cluster.', + 'clusters.create.register.tips': + ' On the Kubernetes cluster that needs to be added, run the following command to join its nodes to the cluster.', 'cluster.create.checkEnv.tips': 'Use the following command to check if the environment is ready' }; diff --git a/src/locales/ja-JP/clusters.ts b/src/locales/ja-JP/clusters.ts index 5645f2d1..ae86c818 100644 --- a/src/locales/ja-JP/clusters.ts +++ b/src/locales/ja-JP/clusters.ts @@ -43,7 +43,9 @@ export default { 'clusters.create.addCommand.tips': ' On the Worker that needs to be added, run the following command to join it to the cluster.', 'cluster.create.checkEnv.tips': - 'Use the following command to check if the environment is ready' + 'Use the following command to check if the environment is ready', + 'clusters.create.register.tips': + ' On the Kubernetes cluster that needs to be added, run the following command to join its nodes to the cluster.' }; // ========== To-Do: Translate Keys (Remove After Translation) ========== @@ -87,6 +89,7 @@ export default { // 38. 'clusters.workerpool.batchSize.desc': 'Number of workers created simultaneously in the Worker pool', // 39. 'clusters.create.addworker.tips': ' Please make sure the prerequisites for {label} are met before executing the following command.', // 40. 'clusters.create.addCommand.tips': ' On the Worker that needs to be added, run the following command to join it to the cluster.', -// 41. 'cluster.create.checkEnv.tips': 'Use the following command to check if the environment is ready' +// 41. 'cluster.create.checkEnv.tips': 'Use the following command to check if the environment is ready', +// 42. 'clusters.create.register.tips': ' On the Kubernetes cluster that needs to be added, run the following command to join its nodes to the cluster.' // ========== End of To-Do List ========== diff --git a/src/locales/ru-RU/clusters.ts b/src/locales/ru-RU/clusters.ts index a1f7716e..72fabe00 100644 --- a/src/locales/ru-RU/clusters.ts +++ b/src/locales/ru-RU/clusters.ts @@ -43,9 +43,11 @@ export default { 'clusters.create.addCommand.tips': 'На воркере, который необходимо добавить, выполните следующую команду, чтобы присоединить его к кластеру.', 'cluster.create.checkEnv.tips': - 'Используйте следующую команду для проверки готовности окружения' + 'Используйте следующую команду для проверки готовности окружения', + 'clusters.create.register.tips': + ' On the Kubernetes cluster that needs to be added, run the following command to join its nodes to the cluster.' }; // ========== To-Do: Translate Keys (Remove After Translation) ========== - +// 1. 'clusters.create.register.tips': ' On the Kubernetes cluster that needs to be added, run the following command to join its nodes to the cluster.', // ========== End of To-Do List ========== diff --git a/src/locales/zh-CN/clusters.ts b/src/locales/zh-CN/clusters.ts index b32ad6bd..42b94077 100644 --- a/src/locales/zh-CN/clusters.ts +++ b/src/locales/zh-CN/clusters.ts @@ -41,5 +41,7 @@ export default { '在执行以下命令之前,请确保已满足 {label} 的先决条件。', 'clusters.create.addCommand.tips': ' 在需要添加的 Worker 上运行以下命令,将其加入到集群中', + 'clusters.create.register.tips': + ' 在需要添加的 Kubernetes 集群上运行以下命令,将其中的节点加入到集群中', 'cluster.create.checkEnv.tips': '使用以下命令检查环境是否准备妥当' }; diff --git a/src/pages/backends/components/backend-card.tsx b/src/pages/backends/components/backend-card.tsx index 5a57dd27..1b14afb9 100644 --- a/src/pages/backends/components/backend-card.tsx +++ b/src/pages/backends/components/backend-card.tsx @@ -1,3 +1,4 @@ +import AutoTooltip from '@/components/auto-tooltip'; import DropDownActions from '@/components/drop-down-actions'; import IconFont from '@/components/icon-font'; import ThemeTag from '@/components/tags-wrapper/theme-tag'; @@ -12,7 +13,7 @@ import { builtInBackendLogos, customColors, customIcons, - gpuColorMap + getGpuColor } from '../config'; import { ListItem } from '../config/types'; @@ -125,7 +126,7 @@ const BackendCard: React.FC = ({ data, onSelect }) => { if (data.is_build_in) { return backendActions.filter((item) => item.key !== 'delete'); } - return backendActions; + return backendActions.filter((item) => item.key !== 'view_versions'); }, [data.is_build_in]); const onClick = (e: React.MouseEvent) => { @@ -146,19 +147,21 @@ const BackendCard: React.FC = ({ data, onSelect }) => { Supported Frameworks: - - {backendList.map((item: string) => { - return ( - - {item} - - ); - })} - + + + {_.take(backendList, 3).map((item: string) => { + return ( + + {item} + + ); + })} + + ); } @@ -169,7 +172,13 @@ const BackendCard: React.FC = ({ data, onSelect }) => { Default Image: - {_.get(data, ['version_configs', data.default_version, 'image_name'])} + + {_.get(data, [ + 'version_configs', + data.default_version, + 'image_name' + ])} + ); @@ -178,7 +187,7 @@ const BackendCard: React.FC = ({ data, onSelect }) => { return ( void; +} + +const ViewBuiltinVersionsModal: React.FC = ({ + open, + currentData, + onClose +}) => { + const [form] = Form.useForm(); + + useEffect(() => { + if (open && currentData) { + const builtInVersions = Object.keys( + currentData.build_in_version_configs || {} + ).map((key) => ({ + version_no: key, + image_name: currentData.build_in_version_configs[key].image_name, + run_command: currentData.build_in_version_configs[key].run_command, + is_default: key === currentData.default_version, + backend_list: + currentData.build_in_version_configs[key].backend_list || [], + is_built_in: true + })); + form.setFieldsValue({ + build_in_version_configs: builtInVersions + }); + } + }, [open, currentData, form]); + + return ( + +
+ + +
+ ); +}; + +export default ViewBuiltinVersionsModal; diff --git a/src/pages/backends/config/index.ts b/src/pages/backends/config/index.ts index dfc92e2f..7d078e6b 100644 --- a/src/pages/backends/config/index.ts +++ b/src/pages/backends/config/index.ts @@ -101,6 +101,19 @@ export const gpuColorMap: Record = { corex: 'purple' }; +export const getGpuColor = (gpuType: string) => { + if (!gpuType) return 'default'; + + if (gpuType.includes('cann')) return gpuColorMap['cann']; + if (gpuType.includes('cuda')) return gpuColorMap['cuda']; + if (gpuType.includes('rocm')) return gpuColorMap['rocm']; + if (gpuType.includes('dtk')) return gpuColorMap['dtk']; + if (gpuType.includes('musa')) return gpuColorMap['musa']; + if (gpuType.includes('corex')) return gpuColorMap['corex']; + + return 'default'; +}; + export const customIcons = ['∑', '∏', '∫', '∂', '∞', 'θ', '∆', '∇']; export const backendFields = [ diff --git a/src/pages/backends/forms/built-in-versions.tsx b/src/pages/backends/forms/built-in-versions.tsx index db75fe61..5b6ee8c7 100644 --- a/src/pages/backends/forms/built-in-versions.tsx +++ b/src/pages/backends/forms/built-in-versions.tsx @@ -1,7 +1,7 @@ import ThemeTag from '@/components/tags-wrapper/theme-tag'; import { Divider, Form } from 'antd'; import styled from 'styled-components'; -import { gpuColorMap } from '../config'; +import { getGpuColor } from '../config'; const ItemWrapper = styled.div` display: flex; @@ -62,7 +62,7 @@ const VersionItem: React.FC = ({ data }) => { {item} diff --git a/src/pages/backends/forms/versions-config.tsx b/src/pages/backends/forms/versions-config.tsx index 4d306a84..676c57f2 100644 --- a/src/pages/backends/forms/versions-config.tsx +++ b/src/pages/backends/forms/versions-config.tsx @@ -2,6 +2,7 @@ import SealInput from '@/components/seal-form/seal-input'; import { PageActionType } from '@/config/types'; import { MinusOutlined, PlusOutlined } from '@ant-design/icons'; import { Button, Divider, Form, Radio } from 'antd'; +import _ from 'lodash'; import React, { useEffect } from 'react'; import styled from 'styled-components'; import { ListItem } from '../config/types'; @@ -20,6 +21,13 @@ const ActionWrapper = styled.div` margin-top: 24px; `; +const Label = styled.div` + line-height: 1; + color: var(--ant-color-text-tertiary); + margin-bottom: 20px; + font-size: var(--font-size-base); +`; + const ItemWrapper = styled.div``; type AddModalProps = { @@ -28,6 +36,7 @@ type AddModalProps = { }; const VersionsForm: React.FC = ({ action, currentData }) => { const form = Form.useFormInstance(); + const version_configs = Form.useWatch('version_configs', form); const versionList = [ { version_no: '', @@ -47,6 +56,26 @@ const VersionsForm: React.FC = ({ action, currentData }) => { form.setFieldValue('version_configs', updatedVersions); }; + const handleVersionOnBlur = ( + e: React.FocusEvent, + index: number + ) => { + const inputValue = e.target.value.trim() || ''; + const versions = form.getFieldValue('version_configs') || []; + console.log('inputValue:', version_configs, currentData); + const isDuplicate = + _.get(currentData, ['build_in_version_configs', inputValue]) || + versions.some( + (version: any, idx: number) => + version?.version_no === inputValue && idx !== index + ); + if (isDuplicate) { + const updatedVersions = [...versions]; + updatedVersions[index].version_no = ''; + form.setFieldValue('version_configs', updatedVersions); + } + }; + useEffect(() => { const versions = form.getFieldValue('version_configs') || []; console.log('versions:', versions); @@ -58,7 +87,6 @@ const VersionsForm: React.FC = ({ action, currentData }) => { return ( {(fields, { add, remove }) => { - console.log('fields:', fields); return (
= ({ action, currentData }) => { padding: '14px' }} > + {fields.map(({ key, name, ...restField }, index) => ( diff --git a/src/pages/backends/list.tsx b/src/pages/backends/list.tsx index 264bfe70..af02e927 100644 --- a/src/pages/backends/list.tsx +++ b/src/pages/backends/list.tsx @@ -17,6 +17,7 @@ import { } from './apis'; import AddModal from './components/add-modal'; import BackendCardList from './components/backend-list'; +import ViewBuiltinVersionsModal from './components/view-builtin-versions-modal'; import { json2Yaml, yaml2Json } from './config'; import { FormData, ListItem } from './config/types'; import useExportYAML from './hooks/use-export-yaml'; @@ -53,6 +54,11 @@ const BackendList = () => { action: PageActionType; currentData?: Partial; }>({ open: false, action: 'create' }); + const [openViewBuiltinVersionsModal, setOpenViewBuiltinVersionsModal] = + useState<{ + open: boolean; + currentData?: Partial; + }>({ open: false }); // build_in_version_configs is read-only, but needs to be included when updating const handleOnSubmit = async (values: FormData) => { @@ -129,6 +135,11 @@ const BackendList = () => { // Export YAML action console.log('Export YAML for:', item.data); exportYAML(item.data); + } else if (item.action === 'view_versions') { + setOpenViewBuiltinVersionsModal({ + open: true, + currentData: item.data + }); } }; @@ -180,6 +191,16 @@ const BackendList = () => { : 'Edit Backend' } > + + setOpenViewBuiltinVersionsModal({ + open: false, + currentData: undefined + }) + } + > ); diff --git a/src/pages/cluster-management/components/add-worker-step.tsx b/src/pages/cluster-management/components/add-worker-step.tsx index b6abd1c4..7bf782ba 100644 --- a/src/pages/cluster-management/components/add-worker-step.tsx +++ b/src/pages/cluster-management/components/add-worker-step.tsx @@ -111,13 +111,21 @@ const AddWorkerStep: React.FC = ({ 1. {intl.formatMessage({ id: 'cluster.create.checkEnv.tips' })}
-
- 2. {intl.formatMessage({ id: 'clusters.create.addCommand.tips' })} -
+ {provider === ProviderValueMap.Kubernetes ? ( - + <> +
+ 2. {intl.formatMessage({ id: 'clusters.create.register.tips' })} +
+ + ) : ( - + <> +
+ 2. {intl.formatMessage({ id: 'clusters.create.addCommand.tips' })} +
+ + )} ); diff --git a/src/pages/llmodels/components/advance-config.tsx b/src/pages/llmodels/components/advance-config.tsx deleted file mode 100644 index 99d68bb8..00000000 --- a/src/pages/llmodels/components/advance-config.tsx +++ /dev/null @@ -1,318 +0,0 @@ -import IconFont from '@/components/icon-font'; -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 { PageActionType } from '@/config/types'; -import { useIntl } from '@umijs/max'; -import { Collapse, Form, FormInstance } from 'antd'; -import _ from 'lodash'; -import { useCallback, useMemo } from 'react'; -import { - getBackendParamsTips, - modelCategories, - placementStrategyOptions, - ScheduleValueMap -} from '../config'; -import BackendParameters, { - 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'; -import Performance from '../forms/performance'; -import dataformStyles from '../style/data-form.less'; - -interface AdvanceConfigProps { - isGGUF: boolean; - form: FormInstance; - gpuOptions: Array; - action: PageActionType; - source: string; -} - -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 backend_parameters = Form.useWatch('backend_parameters', form); - const categories = Form.useWatch('categories', form); - const backend_version = Form.useWatch('backend_version', form); - 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, gpuOptions, source, isGGUF } = useFormContext(); - - const paramsConfig = useMemo(() => { - return _.get(BackendParameters, backend, []); - }, [backend]); - - const backendParamsTips = useMemo(() => { - return getBackendParamsTips(backend); - }, [backend]); - - const handleWorkerLabelsChange = useCallback( - (labels: Record) => { - form.setFieldValue('worker_selector', labels); - }, - [] - ); - const handleEnviromentVarsChange = useCallback( - (labels: Record) => { - form.setFieldValue('env', labels); - }, - [] - ); - - const handleBackendParametersChange = useCallback((list: string[]) => { - form.setFieldValue('backend_parameters', list); - }, []); - - const handleBackendParametersOnBlur = () => { - onValuesChange?.({}, form.getFieldsValue()); - }; - - const handleDeleteBackendParameters = (index: number) => { - onValuesChange?.({}, form.getFieldsValue()); - }; - - 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()); - }; - - const collapseItems = useMemo(() => { - const children = ( - <> - 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 - > - - -
- - ); - return [ - { - key: '2', - label: ( - - Performance - - ), - forceRender: true, - children: - }, - { - key: '1', - label: ( - - {intl.formatMessage({ id: 'resources.form.advanced' })} - - ), - forceRender: true, - children - } - ]; - }, [ - form, - source, - intl, - gpuOptions, - paramsConfig, - scheduleType, - wokerSelector, - backend, - backend_parameters, - isGGUF, - categories, - backend_version, - placement_strategy, - gpuSelectorIds, - EnviromentVars, - worker_selector - ]); - - return ( - ( - - )} - items={collapseItems} - > - ); -}; - -export default AdvanceConfig; diff --git a/src/pages/llmodels/components/deploy-builtin-modal.tsx b/src/pages/llmodels/components/deploy-builtin-modal.tsx index 5f55886b..d31f768b 100644 --- a/src/pages/llmodels/components/deploy-builtin-modal.tsx +++ b/src/pages/llmodels/components/deploy-builtin-modal.tsx @@ -20,10 +20,10 @@ import { backendOptionsMap } from '../config/backend-parameters'; import { CatalogFormContext } from '../config/form-context'; import { CatalogSpec, FormData, ListItem, SourceType } from '../config/types'; import { generateGPUIds } from '../config/utils'; +import DataForm from '../forms'; import { useCheckCompatibility } from '../hooks'; import useFormInitialValues from '../hooks/use-form-initial-values'; import CompatibilityAlert from './compatible-alert'; -import DataForm from './data-form'; const pickFieldsFromSpec = [ 'backend_version', diff --git a/src/pages/llmodels/components/deploy-modal.tsx b/src/pages/llmodels/components/deploy-modal.tsx index 8e72fa0d..c1db2b83 100644 --- a/src/pages/llmodels/components/deploy-modal.tsx +++ b/src/pages/llmodels/components/deploy-modal.tsx @@ -11,6 +11,7 @@ import ColumnWrapper from '../../_components/column-wrapper'; import { defaultFormValues, deployFormKeyMap, modelSourceMap } from '../config'; import { backendOptionsMap } from '../config/backend-parameters'; import { BackendOption, FormData, SourceType } from '../config/types'; +import DataForm from '../forms'; import { MessageStatus, WarningStausOptions, @@ -19,7 +20,6 @@ import { } from '../hooks'; import useCheckBackend from '../hooks/use-check-backend'; import CompatibilityAlert from './compatible-alert'; -import DataForm from './data-form'; import GGUFResult from './gguf-result'; import ModelCard from './model-card'; import SearchModel from './search-model'; @@ -488,6 +488,9 @@ const AddModal: FC = (props) => { = forwardRef((props, ref) => { }, { key: 'advance_config', - label: 'Advanced', + label: intl.formatMessage({ id: 'resources.form.advanced' }), forceRender: true, children: } diff --git a/src/pages/llmodels/forms/performance.tsx b/src/pages/llmodels/forms/performance.tsx index 7f878608..d2ec7860 100644 --- a/src/pages/llmodels/forms/performance.tsx +++ b/src/pages/llmodels/forms/performance.tsx @@ -33,6 +33,7 @@ const Performance: React.FC = () => { const { onQuantizationChange } = useCatalogFormContext(); const { getRuleMessage } = useAppUtils(); const form = Form.useFormInstance(); + const scheduleType = Form.useWatch('scheduleType', form); const handleScheduleTypeChange = (value: string) => { if (value === ScheduleValueMap.Auto) { @@ -61,8 +62,7 @@ const Performance: React.FC = () => { options={scheduleList} > - {form.getFieldValue('scheduleType') === - ScheduleValueMap.SpecificGPUType && ( + {scheduleType === ScheduleValueMap.SpecificGPUType && ( <> { )} - {form.getFieldValue('scheduleType') === ScheduleValueMap.Manual && + {scheduleType === ScheduleValueMap.Manual && !form.getFieldValue('fix_gpu_type') && ( <>