From 0dfe77df3714af54f3c3cf46467b5bbc6b953597 Mon Sep 17 00:00:00 2001 From: jialin Date: Mon, 29 Sep 2025 19:28:27 +0800 Subject: [PATCH] chore: built in versions --- src/components/list-input/index.tsx | 2 +- src/components/templates/card.tsx | 15 ++- src/pages/backends/components/add-modal.tsx | 72 +++++++---- .../backends/components/backend-card.tsx | 74 +++++++++--- src/pages/backends/config/index.ts | 16 +++ src/pages/backends/config/types.ts | 3 + .../backends/forms/built-in-versions.tsx | 114 ++++++++++++++++++ src/pages/backends/forms/index.tsx | 13 +- src/pages/backends/forms/versions-config.tsx | 61 +++++----- src/pages/backends/list.tsx | 9 +- .../components/cloud-options.tsx | 12 -- .../components/support-gpus.tsx | 2 +- 12 files changed, 306 insertions(+), 87 deletions(-) create mode 100644 src/pages/backends/forms/built-in-versions.tsx diff --git a/src/components/list-input/index.tsx b/src/components/list-input/index.tsx index 3375aa2b..d4db84a3 100644 --- a/src/components/list-input/index.tsx +++ b/src/components/list-input/index.tsx @@ -6,7 +6,7 @@ import ListItem from './list-item'; interface ListInputProps { dataList: string[]; - label: React.ReactNode; + label?: React.ReactNode; description?: React.ReactNode; btnText?: string; options?: Global.HintOptions[]; diff --git a/src/components/templates/card.tsx b/src/components/templates/card.tsx index d4b22e04..013fd951 100644 --- a/src/components/templates/card.tsx +++ b/src/components/templates/card.tsx @@ -12,6 +12,7 @@ interface CardProps { footer?: React.ReactNode; icon?: React.ReactNode; active?: boolean; + hoverable?: boolean; disabled?: boolean; onClick?: () => void; } @@ -33,6 +34,11 @@ const CardWrapper = styled.div.attrs({ transition: background-color 0.2s ease; } + &.hoverable:hover:not(.disabled) { + background-color: var(--ant-color-fill-tertiary); + transition: background-color 0.2s ease; + } + &.ghost { background-color: transparent; } @@ -102,19 +108,26 @@ const Card: React.FC = (props) => { icon, active, disabled, + hoverable, onClick } = props; + const handleClick = () => { + if (disabled || !clickable) return; + onClick?.(); + }; + return ( {icon && {icon}} diff --git a/src/pages/backends/components/add-modal.tsx b/src/pages/backends/components/add-modal.tsx index 1cc71109..5972b48b 100644 --- a/src/pages/backends/components/add-modal.tsx +++ b/src/pages/backends/components/add-modal.tsx @@ -92,31 +92,44 @@ const AddModal: React.FC = (props) => { }); }; - const iniFormContent = (values: any) => { - const versionConfigs = Object.keys(values.version_configs || {}).map( - (key) => ({ - version_no: key, - image_name: values.version_configs[key].image_name, - run_command: values.version_configs[key].run_command, - is_default: key === values.default_version, - isBuiltin: true - }) - ); - return { - ...values, - version_configs: versionConfigs - }; - }; - - const initYamlContent = (values: any) => { - if (currentData?.is_build_in) { - return json2Yaml(_.pick(values, backendFields)); - } - return json2Yaml(_.pick(values, [...backendFields, 'default_version'])); - }; - useEffect(() => { - if (action === PageAction.EDIT) { + const iniFormContent = (values: any) => { + const versionConfigs = Object.keys(values.version_configs || {}).map( + (key) => ({ + version_no: key, + image_name: values.version_configs[key].image_name, + run_command: values.version_configs[key].run_command, + is_default: key === values.default_version + }) + ); + + const builtInVersions = Object.keys( + values.build_in_version_configs || {} + ).map((key) => ({ + version_no: key, + image_name: values.build_in_version_configs[key].image_name, + run_command: values.build_in_version_configs[key].run_command, + is_default: key === values.default_version, + backend_list: values.build_in_version_configs[key].backend_list || [], + is_built_in: true + })); + + return { + ...values, + version_configs: versionConfigs, + build_in_version_configs: builtInVersions + }; + }; + + // built-in backend does not allow to edit default_version + const initYamlContent = (values: any) => { + if (currentData?.is_build_in) { + return json2Yaml(_.pick(values, backendFields)); + } + return json2Yaml(_.pick(values, [...backendFields, 'default_version'])); + }; + + if (action === PageAction.EDIT && open) { const yaml = initYamlContent(currentData || {}); const formData = iniFormContent(currentData || {}); setYamlContent(yaml); @@ -124,7 +137,15 @@ const AddModal: React.FC = (props) => { formRef.current?.setFieldsValue?.(formData); editorRef.current?.setContent?.(yaml); } - }, [action, currentData]); + + if (!open) { + formRef.current?.resetFields?.(); + editorRef.current?.setContent?.(''); + setFormContent({} as FormData); + setYamlContent(''); + setActiveKey('form'); + } + }, [action, currentData, open]); return ( = (props) => { /> void; onSelect?: (item: any) => void; @@ -120,17 +128,65 @@ const BackendCard: React.FC = ({ data, onSelect }) => { return backendActions; }, [data.is_build_in]); + const onClick = (e: React.MouseEvent) => { + e.stopPropagation(); + }; + + const renderDrivers = () => { + if (data.is_build_in) { + const backendList = _.get( + data, + ['build_in_version_configs', data.default_version, 'backend_list'], + [] + ); + + return ( + <> + + + Supported Frameworks: + + + {backendList.map((item: string) => { + return ( + + {item} + + ); + })} + + + ); + } + return ( + <> + + + Default Image: + + + {_.get(data, ['version_configs', data.default_version, 'image_name'])} + + + ); + }; + return (
{renderIcon()}
- + = ({ data, onSelect }) => { Default Version: {data.default_version} - - - Default Image: - - - {_.get(data, [ - 'version_configs', - data.default_version, - 'image_name' - ])} - + {renderDrivers()}
diff --git a/src/pages/backends/config/index.ts b/src/pages/backends/config/index.ts index 0827aa27..dfc92e2f 100644 --- a/src/pages/backends/config/index.ts +++ b/src/pages/backends/config/index.ts @@ -38,6 +38,13 @@ export const backendActions = [ icon: icons.EditOutlined, locale: false }, + { + label: 'View Built-in Versions', + value: 'view_versions', + key: 'view_versions', + icon: icons.Version, + locale: false + }, { label: 'Export YAML', value: 'yaml', @@ -85,6 +92,15 @@ export const customColors = [ 'geekblue' ]; +export const gpuColorMap: Record = { + cann: 'orange', + cuda: 'green', + rocm: 'volcano', + dtk: 'magenta', + musa: 'cyan', + corex: 'purple' +}; + export const customIcons = ['∑', '∏', '∫', '∂', '∞', 'θ', '∆', '∇']; export const backendFields = [ diff --git a/src/pages/backends/config/types.ts b/src/pages/backends/config/types.ts index 38e4fe66..0c3501ec 100644 --- a/src/pages/backends/config/types.ts +++ b/src/pages/backends/config/types.ts @@ -2,7 +2,9 @@ export interface VersionConfigs { image_name: string; run_command: string; is_default: boolean; + backend_list?: string[]; version_no?: string; + is_built_in?: boolean; } export interface FormData { @@ -22,4 +24,5 @@ export interface ListItem extends FormData { is_build_in?: boolean; created_at?: string; updated_at?: string; + build_in_version_configs?: Record; } diff --git a/src/pages/backends/forms/built-in-versions.tsx b/src/pages/backends/forms/built-in-versions.tsx new file mode 100644 index 00000000..db75fe61 --- /dev/null +++ b/src/pages/backends/forms/built-in-versions.tsx @@ -0,0 +1,114 @@ +import ThemeTag from '@/components/tags-wrapper/theme-tag'; +import { Divider, Form } from 'antd'; +import styled from 'styled-components'; +import { gpuColorMap } from '../config'; + +const ItemWrapper = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 8px; + .title { + width: 100%; + justify-content: space-between; + display: flex; + align-items: center; + font-weight: 600; + margin-bottom: 8px; + } +`; + +const RowWrapper = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 4px; + .label { + color: var(--ant-color-text-tertiary); + } + .drivers { + display: flex; + flex-wrap: wrap; + gap: 8px; + } +`; + +interface VersionItemProps { + data: { + backend_list: string[]; + version_no: string; + image_name: string; + run_command: string; + isBuiltin: boolean; + is_default: boolean; + }; +} + +const VersionItem: React.FC = ({ data }) => { + return ( + +
+ {data.version_no} + {data.is_default && ( + + Default + + )} +
+ + + {data.backend_list?.map((item) => { + return ( + + {item} + + ); + })} + + +
+ ); +}; + +const BuiltInVersionsForm = () => { + const form = Form.useFormInstance(); + const versionList = Form.useWatch('build_in_version_configs', form) || []; + return ( + + {() => { + return ( +
+ {versionList.map((version: any, index: number) => { + return ( + <> + + {index < versionList.length - 1 && ( + + )} + + ); + })} +
+ ); + }} +
+ ); +}; + +export default BuiltInVersionsForm; diff --git a/src/pages/backends/forms/index.tsx b/src/pages/backends/forms/index.tsx index dcadd7cb..790f28b4 100644 --- a/src/pages/backends/forms/index.tsx +++ b/src/pages/backends/forms/index.tsx @@ -5,6 +5,7 @@ import _ from 'lodash'; import React, { forwardRef, useEffect, useImperativeHandle } from 'react'; import { FormData, ListItem } from '../config/types'; import BasicForm from './basic'; +import BuiltInVersionsForm from './built-in-versions'; import ParametersForm from './parameters'; import VersionsForm from './versions-config'; @@ -76,9 +77,19 @@ const BackendForm: React.FC = forwardRef( accordion={false} onChange={handleOnCollapseChange} items={[ + ...(currentData?.is_build_in + ? [ + { + key: 'builtin_version_configs', + label: 'Built-in Versions', + forceRender: true, + children: + } + ] + : []), { key: 'version_configs', - label: 'Versions', + label: 'Custom Versions', forceRender: true, children: ( = ({ action, currentData }) => { return ( {(fields, { add, remove }) => { + console.log('fields:', fields); return (
= ({ action, currentData }) => { > - + - -
+ {!currentData?.is_build_in && ( = ({ action, currentData }) => { )} - { -
- {fields.length > 1 && ( - - )} -
- } -
+
+ {(fields.length > 1 || currentData?.is_build_in) && ( + + )} +
+ - {index === fields.length - 1 && ( - - )} ))} +
); }} diff --git a/src/pages/backends/list.tsx b/src/pages/backends/list.tsx index cfc3d5e1..264bfe70 100644 --- a/src/pages/backends/list.tsx +++ b/src/pages/backends/list.tsx @@ -4,6 +4,7 @@ import { PageActionType } from '@/config/types'; import useTableFetch from '@/hooks/use-table-fetch'; import { PageContainer } from '@ant-design/pro-components'; import { useIntl } from '@umijs/max'; +import _ from 'lodash'; import { useState } from 'react'; import { createBackend, @@ -53,6 +54,7 @@ const BackendList = () => { currentData?: Partial; }>({ open: false, action: 'create' }); + // build_in_version_configs is read-only, but needs to be included when updating const handleOnSubmit = async (values: FormData) => { try { if (openModalStatus.action === 'create') { @@ -60,7 +62,9 @@ const BackendList = () => { } else { await updateBackend(openModalStatus.currentData!.id!, { data: { - ...values + build_in_version_configs: + openModalStatus.currentData?.build_in_version_configs, + ..._.omit(values, ['build_in_version_configs']) } }); } @@ -73,6 +77,7 @@ const BackendList = () => { } catch (error) {} }; + // build_in_version_configs needs to be included when updating from YAML, but not allowed to be changed const handleOnSubmitYaml = async (values: { content: string }) => { try { if (openModalStatus.action === 'create') { @@ -82,6 +87,8 @@ const BackendList = () => { const yamlContent = json2Yaml({ backend_name: openModalStatus.currentData?.backend_name, default_version: openModalStatus.currentData?.default_version, + build_in_version_configs: + openModalStatus.currentData?.build_in_version_configs, ...jsonData }); await updateBackendFromYAML(openModalStatus.currentData!.id!, { diff --git a/src/pages/cluster-management/components/cloud-options.tsx b/src/pages/cluster-management/components/cloud-options.tsx index 92bfab70..ba05206e 100644 --- a/src/pages/cluster-management/components/cloud-options.tsx +++ b/src/pages/cluster-management/components/cloud-options.tsx @@ -22,18 +22,6 @@ const Title = styled.div` font-weight: 600; `; -const ButtonWrapper = styled.span` - display: flex; - align-items: center; - height: 100%; - font-weight: 400; - cursor: pointer; - gap: 8px; - &:hover { - color: var(--ant-color-text-secondary); - } -`; - const CloudOptions: React.FC<{ ref?: any; }> = forwardRef((props, ref) => { diff --git a/src/pages/cluster-management/components/support-gpus.tsx b/src/pages/cluster-management/components/support-gpus.tsx index 9253d1c8..a0aa045f 100644 --- a/src/pages/cluster-management/components/support-gpus.tsx +++ b/src/pages/cluster-management/components/support-gpus.tsx @@ -35,7 +35,7 @@ const SupportedHardware: React.FC = ({ const supportedHardPlatforms = [ { - label: intl.formatMessage({ id: 'vendor.nividia' }), + label: 'Nvidia', value: 'cuda', description: '', key: 'cuda',