From b5fa40047ad87aa870f195b76ac6afe456b6ac8d Mon Sep 17 00:00:00 2001 From: jialin Date: Mon, 13 Oct 2025 11:48:02 +0800 Subject: [PATCH] chore: pluggable locale config --- config/routes.ts | 2 +- src/components/collapse-container/index.tsx | 14 +- src/locales/en-US/backends.ts | 25 +- src/locales/ja-JP/backends.ts | 25 +- src/locales/ru-RU/backends.ts | 25 +- src/locales/zh-CN/backends.ts | 25 +- src/pages/backends/components/add-modal.tsx | 9 +- .../backends/components/backend-card.tsx | 106 +++-- .../components/version-info-modal.tsx | 89 ++++ .../view-builtin-versions-modal.tsx | 76 ---- src/pages/backends/components/yaml-editor.tsx | 23 - src/pages/backends/config/index.ts | 24 +- src/pages/backends/config/types.ts | 7 +- src/pages/backends/forms/basic.tsx | 17 +- .../backends/forms/built-in-versions.tsx | 119 ------ src/pages/backends/forms/index.tsx | 3 +- src/pages/backends/forms/parameters.tsx | 32 -- src/pages/backends/forms/version-info.tsx | 185 ++++++++ src/pages/backends/forms/versions-config.tsx | 399 +++++++++--------- src/pages/backends/{list.tsx => index.tsx} | 31 +- .../components/support-gpus.tsx | 2 +- src/pages/llmodels/apis/index.ts | 5 +- .../components/deploy-builtin-modal.tsx | 5 +- .../llmodels/components/deploy-modal.tsx | 23 +- src/pages/llmodels/components/table-list.tsx | 7 +- .../llmodels/components/update-modal.tsx | 4 +- .../config/backend-parameters/index.ts | 4 +- src/pages/llmodels/forms/index.tsx | 8 +- .../llmodels/hooks/use-query-backends.ts | 9 +- .../resources/components/model-files.tsx | 3 - 30 files changed, 731 insertions(+), 575 deletions(-) create mode 100644 src/pages/backends/components/version-info-modal.tsx delete mode 100644 src/pages/backends/components/view-builtin-versions-modal.tsx delete mode 100644 src/pages/backends/forms/built-in-versions.tsx delete mode 100644 src/pages/backends/forms/parameters.tsx create mode 100644 src/pages/backends/forms/version-info.tsx rename src/pages/backends/{list.tsx => index.tsx} (89%) diff --git a/config/routes.ts b/config/routes.ts index 003df2c5..3f1c0ed5 100644 --- a/config/routes.ts +++ b/config/routes.ts @@ -115,7 +115,7 @@ export default [ defaultIcon: 'icon-backend', access: 'canSeeAdmin', subMenu: ['/models/backends/create'], - component: './backends/list' + component: './backends/index' } ] }, diff --git a/src/components/collapse-container/index.tsx b/src/components/collapse-container/index.tsx index 67af3c51..8255755c 100644 --- a/src/components/collapse-container/index.tsx +++ b/src/components/collapse-container/index.tsx @@ -79,6 +79,11 @@ export interface CollapsibleContainerProps { variant?: 'outlined' | 'borderless' | undefined; className?: string; children?: React.ReactNode; + styles?: { + body?: React.CSSProperties; + header?: React.CSSProperties; + content?: React.CSSProperties; + }; } export default function CollapsibleContainer({ @@ -94,6 +99,7 @@ export default function CollapsibleContainer({ variant = 'borderless', className = '', collapsible, + styles: cardStyles, children }: CollapsibleContainerProps) { const { styles } = useStyles(); @@ -169,7 +175,11 @@ export default function CollapsibleContainer({ variant={variant} styles={{ body: { - padding: 0 + padding: 0, + ...cardStyles?.body + }, + header: { + ...cardStyles?.header } }} title={renderTitle()} @@ -181,7 +191,7 @@ export default function CollapsibleContainer({ overflow: 'hidden' }} > -
{children}
+
{children}
); diff --git a/src/locales/en-US/backends.ts b/src/locales/en-US/backends.ts index e4eeb76e..7bd6fa91 100644 --- a/src/locales/en-US/backends.ts +++ b/src/locales/en-US/backends.ts @@ -1,3 +1,26 @@ export default { - 'backends.title': 'Inference Backends' + 'backends.title': 'Inference Backends', + 'backend.button.add': 'Add Backend', + 'backend.button.edit': 'Edit Backend', + 'backend.versions': 'Version List', + 'backend.defaultVersion': 'Default Version', + 'backend.version': 'Version', + 'backend.imageName': 'Image Name', + 'backend.runCommand': 'Execution Command', + 'backend.framework': 'Framework', + 'backend.builtin': 'Built-in', + 'backend.custom': 'Custom', + 'backend.isDefault': 'Default', + 'backend.availableFrameworks': 'Available Frameworks', + 'backend.addVersion': 'Add Version', + 'backend.filter.framework': 'Filter by Framework', + 'backend.export.yaml': 'Export YAML', + 'backend.mode.form': 'Form Mode', + 'backend.mode.yaml': 'YAML Mode', + 'backend.form.healthCheckPath': 'Health Check Path', + 'backend.form.defaultExecuteCommand': 'Default Execution Command', + 'backend.form.defaultBackendParameters': 'Default Backend Parameters', + 'backend.form.versionConfig': 'Versions Config', + 'backend.form.addParameter': 'Add Parameter', + 'backend.form.noVersion': 'No versions added' }; diff --git a/src/locales/ja-JP/backends.ts b/src/locales/ja-JP/backends.ts index e4eeb76e..7bd6fa91 100644 --- a/src/locales/ja-JP/backends.ts +++ b/src/locales/ja-JP/backends.ts @@ -1,3 +1,26 @@ export default { - 'backends.title': 'Inference Backends' + 'backends.title': 'Inference Backends', + 'backend.button.add': 'Add Backend', + 'backend.button.edit': 'Edit Backend', + 'backend.versions': 'Version List', + 'backend.defaultVersion': 'Default Version', + 'backend.version': 'Version', + 'backend.imageName': 'Image Name', + 'backend.runCommand': 'Execution Command', + 'backend.framework': 'Framework', + 'backend.builtin': 'Built-in', + 'backend.custom': 'Custom', + 'backend.isDefault': 'Default', + 'backend.availableFrameworks': 'Available Frameworks', + 'backend.addVersion': 'Add Version', + 'backend.filter.framework': 'Filter by Framework', + 'backend.export.yaml': 'Export YAML', + 'backend.mode.form': 'Form Mode', + 'backend.mode.yaml': 'YAML Mode', + 'backend.form.healthCheckPath': 'Health Check Path', + 'backend.form.defaultExecuteCommand': 'Default Execution Command', + 'backend.form.defaultBackendParameters': 'Default Backend Parameters', + 'backend.form.versionConfig': 'Versions Config', + 'backend.form.addParameter': 'Add Parameter', + 'backend.form.noVersion': 'No versions added' }; diff --git a/src/locales/ru-RU/backends.ts b/src/locales/ru-RU/backends.ts index e4eeb76e..7bd6fa91 100644 --- a/src/locales/ru-RU/backends.ts +++ b/src/locales/ru-RU/backends.ts @@ -1,3 +1,26 @@ export default { - 'backends.title': 'Inference Backends' + 'backends.title': 'Inference Backends', + 'backend.button.add': 'Add Backend', + 'backend.button.edit': 'Edit Backend', + 'backend.versions': 'Version List', + 'backend.defaultVersion': 'Default Version', + 'backend.version': 'Version', + 'backend.imageName': 'Image Name', + 'backend.runCommand': 'Execution Command', + 'backend.framework': 'Framework', + 'backend.builtin': 'Built-in', + 'backend.custom': 'Custom', + 'backend.isDefault': 'Default', + 'backend.availableFrameworks': 'Available Frameworks', + 'backend.addVersion': 'Add Version', + 'backend.filter.framework': 'Filter by Framework', + 'backend.export.yaml': 'Export YAML', + 'backend.mode.form': 'Form Mode', + 'backend.mode.yaml': 'YAML Mode', + 'backend.form.healthCheckPath': 'Health Check Path', + 'backend.form.defaultExecuteCommand': 'Default Execution Command', + 'backend.form.defaultBackendParameters': 'Default Backend Parameters', + 'backend.form.versionConfig': 'Versions Config', + 'backend.form.addParameter': 'Add Parameter', + 'backend.form.noVersion': 'No versions added' }; diff --git a/src/locales/zh-CN/backends.ts b/src/locales/zh-CN/backends.ts index a2ae7ed9..a20c8468 100644 --- a/src/locales/zh-CN/backends.ts +++ b/src/locales/zh-CN/backends.ts @@ -1,3 +1,26 @@ export default { - 'backends.title': '推理后端' + 'backends.title': '推理后端', + 'backend.button.add': '添加后端', + 'backend.button.edit': '编辑后端', + 'backend.versions': '版本列表', + 'backend.defaultVersion': '默认版本', + 'backend.version': '版本', + 'backend.imageName': '镜像名称', + 'backend.runCommand': '执行命令', + 'backend.framework': '框架', + 'backend.builtin': '内置', + 'backend.custom': '自定义', + 'backend.isDefault': '默认', + 'backend.availableFrameworks': '可用框架', + 'backend.addVersion': '添加版本', + 'backend.filter.framework': '按框架过滤', + 'backend.export.yaml': '导出 YAML', + 'backend.mode.form': '表单模式', + 'backend.mode.yaml': 'YAML 模式', + 'backend.form.healthCheckPath': '健康检查路径', + 'backend.form.defaultExecuteCommand': '默认执行命令', + 'backend.form.defaultBackendParameters': '默认后端参数', + 'backend.form.versionConfig': '版本配置', + 'backend.form.addParameter': '添加参数', + 'backend.form.noVersion': '尚未添加版本' }; diff --git a/src/pages/backends/components/add-modal.tsx b/src/pages/backends/components/add-modal.tsx index 8b86d2ea..5ad8d893 100644 --- a/src/pages/backends/components/add-modal.tsx +++ b/src/pages/backends/components/add-modal.tsx @@ -67,7 +67,6 @@ const AddModal: React.FC = (props) => { }; const onFinish = (values: FormData) => { - console.log('Form values:', values); const versionConfigs = values.version_configs?.reduce( (acc: Record, curr) => { if (curr.version_no) { @@ -176,12 +175,12 @@ const AddModal: React.FC = (props) => { onChange={(value) => setActiveKey(value as string)} options={[ { - label: 'Form', + label: intl.formatMessage({ id: 'backend.mode.form' }), value: 'form', icon: }, { - label: 'YAML', + label: intl.formatMessage({ id: 'backend.mode.yaml' }), value: 'yaml', icon: } @@ -207,7 +206,7 @@ const AddModal: React.FC = (props) => { items={[ { key: 'form', - label: 'Form', + label: intl.formatMessage({ id: 'backend.mode.form' }), children: ( = (props) => { }, { key: 'yaml', - label: 'YAML', + label: intl.formatMessage({ id: 'backend.mode.yaml' }), children: ( = ({ data, onSelect }) => { + const intl = useIntl(); + const handleOnSelect = (item: any) => { onSelect?.({ action: item.key, data: data }); }; const handleClick = () => { - onSelect?.({ action: 'edit', data: data }); + onSelect?.({ action: 'view_versions', data: data }); }; const renderIcon = () => { @@ -133,28 +151,30 @@ const BackendCard: React.FC = ({ data, onSelect }) => { e.stopPropagation(); }; - const renderDrivers = () => { - if (data.is_build_in) { - const backendList = _.get( - data, - [ - 'build_in_version_configs', - data.default_version, - 'build_in_frameworks' - ], - [] - ); + const renderFrameworks = () => { + const frameworks = _.keys(data.framework_index_map || {}); - return ( - <> - - - Supported Frameworks: + return ( + + + + + {intl.formatMessage({ id: 'backend.availableFrameworks' })}:{' '} - - - {_.take(backendList, 3).map((item: string) => { - return ( + + + + {_.take(frameworks, 3).map((item: string) => { + return ( +
+ = ({ data, onSelect }) => { > {item} - ); - })} - - - - ); - } - return ( - <> - - - Default Image: - - - - {_.get(data, [ - 'version_configs', - data.default_version, - 'image_name' - ])} - - - + +
+ ); + })} +
+
); }; @@ -194,7 +197,7 @@ const BackendCard: React.FC = ({ data, onSelect }) => { clickable={true} hoverable={true} disabled={false} - height={160} + height={140} ghost header={
@@ -226,18 +229,11 @@ const BackendCard: React.FC = ({ data, onSelect }) => { bordered={false} style={{ borderRadius: 'var(--ant-border-radius)' }} > - Built-in + {intl.formatMessage({ id: 'backend.builtin' })} )} -
- - - Default Version: - - {data.default_version} - {renderDrivers()} -
+ {renderFrameworks()} ); diff --git a/src/pages/backends/components/version-info-modal.tsx b/src/pages/backends/components/version-info-modal.tsx new file mode 100644 index 00000000..2ace0475 --- /dev/null +++ b/src/pages/backends/components/version-info-modal.tsx @@ -0,0 +1,89 @@ +import ScrollerModal from '@/components/scroller-modal'; +import { useIntl } from '@umijs/max'; +import { useEffect, useState } from 'react'; +import { VersionListItem } from '../config/types'; +import VersionInfo from '../forms/version-info'; + +interface VersionInfoModalProps { + open?: boolean; + currentData?: any; + onClose?: () => void; +} + +const VersionInfoModal: React.FC = ({ + open, + currentData, + onClose +}) => { + const intl = useIntl(); + const [versionConfigs, setVersionConfigs] = useState([]); + + useEffect(() => { + if (open && currentData) { + // add is_built_in field to build_in_version_configs + const builtInVersions = currentData.build_in_version_configs || {}; + + for (const key in builtInVersions) { + if (builtInVersions?.hasOwnProperty(key)) { + builtInVersions[key].is_built_in = true; + } + } + + const versions = { + ...builtInVersions, + ...currentData.version_configs + }; + + const versionList: VersionListItem[] = Object.entries(versions).map( + ([key, value]: [string, any]) => ({ + version_no: key, + image_name: value.image_name, + run_command: value.run_command, + is_default: key === currentData.default_version, + availableFrameworks: [ + ...(value.build_in_frameworks || []), + ...(value.custom_framework ? [value.custom_framework] : []) + ], + is_built_in: value.is_built_in || false + }) + ); + + setVersionConfigs(versionList); + } + }, [open, currentData]); + + return ( + + + + ); +}; + +export default VersionInfoModal; diff --git a/src/pages/backends/components/view-builtin-versions-modal.tsx b/src/pages/backends/components/view-builtin-versions-modal.tsx deleted file mode 100644 index b9fa5d0c..00000000 --- a/src/pages/backends/components/view-builtin-versions-modal.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import ScrollerModal from '@/components/scroller-modal'; -import { Form } from 'antd'; -import { useEffect } from 'react'; -import BuiltinVersions from '../forms/built-in-versions'; - -interface ViewBuiltinVersionsModalProps { - open?: boolean; - currentData?: any; - onClose?: () => 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, - build_in_frameworks: - currentData.build_in_version_configs[key].build_in_frameworks || [], - is_built_in: true - })); - form.setFieldsValue({ - build_in_version_configs: builtInVersions - }); - } - }, [open, currentData, form]); - - return ( - -
- - -
- ); -}; - -export default ViewBuiltinVersionsModal; diff --git a/src/pages/backends/components/yaml-editor.tsx b/src/pages/backends/components/yaml-editor.tsx index 8e9fca91..4d5601fc 100644 --- a/src/pages/backends/components/yaml-editor.tsx +++ b/src/pages/backends/components/yaml-editor.tsx @@ -74,29 +74,6 @@ const ViewerEditor: React.FC = forwardRef((props, ref) => { editorRef.current = editor; monacoRef.current = monaco; const uri = monaco.Uri.parse(path); - - // monaco.editor.onDidChangeMarkers((uris: string[]) => { - // if (!uris.some((u) => u.toString() === uri.toString())) return; - - // const model = monaco.editor.getModel(uri); - // if (!model) return; - // console.log('Markers changed:', uris); - // const markers = monaco.editor.getModelMarkers({ resource: uri }); - // const adjusted = markers.map((m: any) => { - // if (m._adjusted) return m; - // return { - // ...m, - // _adjusted: true, - // severity: monaco.MarkerSeverity.ErrorText - // }; - // }); - - // monaco.editor.setModelMarkers( - // monaco.editor.getModel(uri), - // 'yaml', - // adjusted - // ); - // }); }; const formatCode = () => { diff --git a/src/pages/backends/config/index.ts b/src/pages/backends/config/index.ts index f4cbae45..a6274a01 100644 --- a/src/pages/backends/config/index.ts +++ b/src/pages/backends/config/index.ts @@ -18,8 +18,8 @@ const SEAL_SCHEMA = jsYaml.DEFAULT_SCHEMA.extend([SealYamlType]); export const builtInBackends = { SGLang: 'SGLang', - vLLM: 'vllm', - MindIE: 'mindie', + vLLM: 'vLLM', + MindIE: 'MindIE', VoxBox: 'voxbox' }; @@ -32,25 +32,25 @@ export const builtInBackendLogos: Record = { export const backendActions = [ { - label: 'Edit', + label: 'common.button.edit', value: 'edit', key: 'edit', - icon: icons.EditOutlined, - locale: false + locale: true, + icon: icons.EditOutlined }, { - label: 'Export YAML', + label: 'backend.export.yaml', value: 'yaml', key: 'export', - icon: icons.Yaml, - locale: false + locale: true, + icon: icons.Yaml }, { - label: 'Delete', + label: 'common.button.delete', value: 'delete', key: 'delete', icon: icons.DeleteOutlined, - locale: false, + locale: true, danger: true } ]; @@ -136,6 +136,10 @@ export const frameworks = [ { label: 'CoreX', value: 'corex' + }, + { + label: 'CPU', + value: 'cpu' } ]; diff --git a/src/pages/backends/config/types.ts b/src/pages/backends/config/types.ts index 4360635f..85f4dd5c 100644 --- a/src/pages/backends/config/types.ts +++ b/src/pages/backends/config/types.ts @@ -3,11 +3,15 @@ export interface VersionConfigs { run_command: string; is_default: boolean; build_in_frameworks?: string[]; - custom_framework: string; + custom_framework?: string; version_no?: string; is_built_in?: boolean; } +export interface VersionListItem extends VersionConfigs { + availableFrameworks: string[]; +} + export interface FormData { backend_name: string; description?: string; @@ -26,4 +30,5 @@ export interface ListItem extends FormData { created_at?: string; updated_at?: string; build_in_version_configs?: Record; + framework_index_map?: Record; } diff --git a/src/pages/backends/forms/basic.tsx b/src/pages/backends/forms/basic.tsx index 2729b95a..c32201bb 100644 --- a/src/pages/backends/forms/basic.tsx +++ b/src/pages/backends/forms/basic.tsx @@ -38,11 +38,18 @@ const BasicForm: React.FC = ({ action }) => { name="health_check_path" rules={[{ required: false }]} > - + - + @@ -54,8 +61,10 @@ const BasicForm: React.FC = ({ action }) => { onChange={(data: string[]) => { form.setFieldValue('default_backend_param', data); }} - btnText={'Add Backend Parameter'} - label={'Default Backend Parameters'} + btnText={intl.formatMessage({ id: 'backend.form.addParameter' })} + label={intl.formatMessage({ + id: 'backend.form.defaultBackendParameters' + })} > diff --git a/src/pages/backends/forms/built-in-versions.tsx b/src/pages/backends/forms/built-in-versions.tsx deleted file mode 100644 index f04f9e80..00000000 --- a/src/pages/backends/forms/built-in-versions.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import ThemeTag from '@/components/tags-wrapper/theme-tag'; -import { Divider, Form } from 'antd'; -import styled from 'styled-components'; -import { getGpuColor } from '../config'; - -const ItemWrapper = styled.div` - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 8px; - width: 100%; - .title { - width: 100%; - justify-content: space-between; - display: flex; - align-items: center; - font-weight: 600; - } -`; - -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: { - build_in_frameworks: string[]; - custom_framework: string; - version_no: string; - image_name: string; - run_command: string; - isBuiltin: boolean; - is_default: boolean; - }; -} - -export const VersionItem: React.FC = ({ data }) => { - return ( - -
- {data.version_no} - {data.is_default && ( - - Default - - )} -
- - - {data.build_in_frameworks?.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 b3aeca8f..3f96a642 100644 --- a/src/pages/backends/forms/index.tsx +++ b/src/pages/backends/forms/index.tsx @@ -1,4 +1,5 @@ import { PageActionType } from '@/config/types'; +import { useIntl } from '@umijs/max'; import { Form } from 'antd'; import _ from 'lodash'; import React, { forwardRef, useEffect, useImperativeHandle } from 'react'; @@ -14,6 +15,7 @@ type AddModalProps = { }; const BackendForm: React.FC = forwardRef( ({ action, currentData, onFinish }, ref) => { + const intl = useIntl(); const [form] = Form.useForm(); const [activeKey, setActiveKey] = React.useState([]); @@ -33,7 +35,6 @@ const BackendForm: React.FC = forwardRef( useEffect(() => { if (currentData) { - console.log('currentData:', currentData); form.setFieldsValue(currentData); } }, [currentData]); diff --git a/src/pages/backends/forms/parameters.tsx b/src/pages/backends/forms/parameters.tsx deleted file mode 100644 index c1ccadb7..00000000 --- a/src/pages/backends/forms/parameters.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import ListInput from '@/components/list-input'; -import { PageActionType } from '@/config/types'; -import { Form } from 'antd'; -import React from 'react'; -import { FormData } from '../config/types'; - -type AddModalProps = { - action: PageActionType; -}; -const ParametersForm: React.FC = ({ action }) => { - const form = Form.useFormInstance(); - - return ( - <> - - name="default_backend_param" - rules={[{ required: false }]} - > - { - form.setFieldValue('default_backend_param', data); - }} - btnText={'Add Backend Parameter'} - label={'Default Backend Parameters'} - > - - - ); -}; - -export default ParametersForm; diff --git a/src/pages/backends/forms/version-info.tsx b/src/pages/backends/forms/version-info.tsx new file mode 100644 index 00000000..e34f5b28 --- /dev/null +++ b/src/pages/backends/forms/version-info.tsx @@ -0,0 +1,185 @@ +import AutoTooltip from '@/components/auto-tooltip'; +import BaseSelect from '@/components/seal-form/base/select'; +import ThemeTag from '@/components/tags-wrapper/theme-tag'; +import { useIntl } from '@umijs/max'; +import { Empty } from 'antd'; +import { useState } from 'react'; +import styled from 'styled-components'; +import { frameworks, getGpuColor } from '../config'; +import { VersionListItem } from '../config/types'; + +const ItemWrapper = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 8px; + width: 100%; + border: 1px solid var(--ant-color-border); + border-radius: var(--ant-border-radius); + padding: 12px 16px; + &:hover { + background-color: var(--ant-color-fill-tertiary); + } + .title { + width: 100%; + justify-content: space-between; + display: flex; + align-items: center; + font-weight: 600; + } +`; + +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; + } +`; + +const InfoItem = styled.div` + display: grid; + grid-template-columns: max-content 1fr; + gap: 8px; + .label { + color: var(--ant-color-text-tertiary); + } +`; + +const ListWrapper = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 16px; + width: 100%; +`; + +const FilterBox = styled.div` + position: sticky; + top: 0; + width: 100%; + z-index: 100; + padding-bottom: 16px; + padding-top: 16px; + background-color: var(--ant-color-bg-container); +`; + +interface VersionItemProps { + data: VersionListItem; +} + +export const VersionItem: React.FC = ({ data }) => { + const intl = useIntl(); + return ( + +
+ {data.version_no} + {data.is_built_in && ( + + {intl.formatMessage({ id: 'backend.builtin' })} + + )} +
+ + {data.image_name && ( + + + {intl.formatMessage({ id: 'backend.imageName' })}: + + + {data.is_built_in + ? 'Selected dynamically at runtime' + : data.image_name} + + + )} + {data.run_command && ( + + + {intl.formatMessage({ id: 'backend.runCommand' })}: + + + {data.run_command} + + + )} + + + {intl.formatMessage({ id: 'backend.framework' })}: + + + {data.availableFrameworks?.map((item) => { + return ( + + {item} + + ); + })} + + + +
+ ); +}; + +const VersionList: React.FC<{ versionConfigs: VersionListItem[] }> = ({ + versionConfigs +}) => { + const intl = useIntl(); + const [dataList, setDataList] = useState(versionConfigs); + + const handleChange = (value: string) => { + if (value) { + const filtered = versionConfigs.filter((item) => + item.availableFrameworks?.includes(value) + ); + setDataList(filtered); + } else { + setDataList(versionConfigs); + } + }; + + return ( +
+ + + + + {dataList.length ? ( + dataList.map((version: VersionListItem, index: number) => { + return ; + }) + ) : ( + + )} + +
+ ); +}; + +export default VersionList; diff --git a/src/pages/backends/forms/versions-config.tsx b/src/pages/backends/forms/versions-config.tsx index a453157f..209bb0e2 100644 --- a/src/pages/backends/forms/versions-config.tsx +++ b/src/pages/backends/forms/versions-config.tsx @@ -1,16 +1,15 @@ -import AutoTooltip from '@/components/auto-tooltip'; import CollapsibleContainer from '@/components/collapse-container'; 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 { MinusOutlined, PlusOutlined } from '@ant-design/icons'; -import { Button, Divider, Form, Radio } from 'antd'; -import _ from 'lodash'; -import React, { useEffect } from 'react'; +import { useIntl } from '@umijs/max'; +import { Button, Empty, Form, Select, Tag } from 'antd'; +import React, { useEffect, useMemo } from 'react'; import styled from 'styled-components'; import { frameworks } from '../config'; import { ListItem } from '../config/types'; -import { VersionItem } from './built-in-versions'; const Box = styled.div` display: grid; @@ -26,86 +25,66 @@ const Label = styled.span` color: var(--ant-color-text-secondary); `; -const ImageInner = styled.div` - display: flex; - align-items: center; - gap: 8px; - margin-left: 20px; +const DefaultTag = styled(Tag)` + font-weight: 500; + margin-left: 8px; + color: var(--ant-color-text); `; const Title = styled.div` + position: sticky; + top: -16px; + z-index: 100; display: flex; align-items: center; justify-content: space-between; + background-color: var(--ant-color-bg-container); font-weight: 600; - margin-bottom: 8px; -`; - -const VersionItemWrapper = styled.div` - display: flex; - flex-direction: column; - align-items: flex-start; - padding: 8px 16px; - border-radius: var(--ant-border-radius); - background-color: var(--ant-color-fill-quaternary); - width: 100%; + padding-top: 8px; + padding-bottom: 8px; `; type AddModalProps = { action: PageActionType; currentData?: ListItem; - activeKey?: number[]; + activeKey?: Array; }; const VersionsForm: React.FC = ({ action, currentData, activeKey }) => { + const intl = useIntl(); + const { getRuleMessage } = useAppUtils(); + const [defaultVersion, setDefaultVersion] = React.useState( + null + ); const defaultCollapseKey = action === 'edit' ? new Set() : new Set([0]); const form = Form.useFormInstance(); const version_configs = Form.useWatch('version_configs', form); - const buildInVersionConfigs = Form.useWatch('build_in_version_configs', form); const [collapseKey, setCollapseKey] = - React.useState>(defaultCollapseKey); + React.useState>(defaultCollapseKey); const versionList = [ { version_no: '', image_name: '', run_command: '', isBuiltin: false, - is_default: true + is_default: false } ]; - const handleSetDefaultVersion = (e: any, index: number) => { - const versions = form.getFieldValue('version_configs') || []; - const updatedVersions = versions.map((version: any, idx: number) => ({ - ...version, - is_default: idx === index - })); - 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); - } - }; + const validVersions = useMemo(() => { + return (version_configs || []) + .filter((v: any) => v.version_no) + ?.map((v: any) => { + return { + label: v.version_no, + value: v.version_no + }; + }); + }, [version_configs]); const onToggle = (open: boolean, key: number) => { setCollapseKey(open ? new Set([key]) : new Set()); @@ -123,23 +102,11 @@ const VersionsForm: React.FC = ({ form.setFieldValue('version_configs', [...versions, newVersion]); }; - const remove = (versions: any[], index: number) => { - const updatedVersions = versions.filter( - (_: any, idx: number) => idx !== index - ); - // If the removed version was the default, set the first version as default - if (versions[index].is_default && updatedVersions.length > 0) { - updatedVersions[0].is_default = true; - } - form.setFieldValue('version_configs', updatedVersions); - }; - const handleAdd = async () => { try { const isValid = await form.validateFields(['version_configs'], { recursive: true }); - console.log('isValid:', isValid, version_configs); if (isValid) { add(); setTimeout(() => { @@ -154,6 +121,16 @@ const VersionsForm: React.FC = ({ } }; + const handleDefaultChange = (value: string) => { + const versions = form.getFieldValue('version_configs') || []; + const updatedVersions = versions.map((version: any, idx: number) => ({ + ...version, + is_default: version.version_no === value + })); + form.setFieldValue('version_configs', updatedVersions); + setDefaultVersion(value); + }; + useEffect(() => { const versions = form.getFieldValue('version_configs') || []; @@ -168,141 +145,185 @@ const VersionsForm: React.FC = ({ } }, [activeKey]); + useEffect(() => { + if (currentData?.default_version) { + setDefaultVersion(currentData.default_version); + } + }, [currentData?.default_version]); + return ( <> - - + + <span className="flex-center gap-8"> + <span> + {intl.formatMessage({ id: 'backend.form.versionConfig' })} + </span> + <Button onClick={handleAdd} type="link"> + <PlusOutlined /> {intl.formatMessage({ id: 'backend.addVersion' })} + </Button> + </span> + {!currentData?.is_build_in && ( + <Select + prefix={ + <span style={{ color: 'var(--ant-color-text-tertiary)' }}> + {intl.formatMessage({ id: 'backend.isDefault' })}: + </span> + } + value={defaultVersion} + notFoundContent={ + <Empty + style={{ marginBlock: 8 }} + image={Empty.PRESENTED_IMAGE_SIMPLE} + description={intl.formatMessage({ + id: 'backend.form.noVersion' + })} + /> + } + placeholder={intl.formatMessage({ id: 'backend.version' })} + options={validVersions} + style={{ width: 200, fontWeight: 400 }} + allowClear + onChange={handleDefaultChange} + /> + )} +
- - <span>Versions Config</span> - <Button onClick={handleAdd} variant="filled" color="default"> - <PlusOutlined /> Add Version - </Button> - - {buildInVersionConfigs?.map((item: any, index: number) => ( - <> - - - - - - ))} - {version_configs?.map((item: any, index: number) => ( - <> - - Version: - {item.version_no} - - } - subtitle={ - item.image_name && ( - - Image: - - {item.image_name} - - - ) - } - onToggle={(open) => onToggle(open, index)} - deleteBtn={false} - right={ -
- e.stopPropagation()} - > - {!currentData?.is_build_in && ( - - - handleSetDefaultVersion(e, index) - } - > - Default Version - - - )} - - {(version_configs.length > 1 || currentData?.is_build_in) && ( - - )} -
- } - > - - - - - - - - - - - - - - -
- {index < version_configs.length - 1 && ( - + {( + fields: Array<{ key: React.Key; name: number }>, + { add, remove } + ) => { + const versionConfigs = form.getFieldValue('version_configs'); + return fields?.map(({ key, name }) => ( +
- )} - - ))} + > + + + {intl.formatMessage({ id: 'backend.version' })}: + + {versionConfigs[name]?.version_no} + {versionConfigs[name]?.is_default && ( + + {intl.formatMessage({ id: 'backend.isDefault' })} + + )} + + } + onToggle={(open) => onToggle(open, name)} + deleteBtn={false} + right={ +
+ e.stopPropagation()} + > + {!currentData?.is_build_in && ( + + )} + + {(fields.length > 1 || currentData?.is_build_in) && ( + + )} +
+ } + > + + + + + + + + + + + + + + +
+
+ )); + }} +
); diff --git a/src/pages/backends/list.tsx b/src/pages/backends/index.tsx similarity index 89% rename from src/pages/backends/list.tsx rename to src/pages/backends/index.tsx index af02e927..bae65489 100644 --- a/src/pages/backends/list.tsx +++ b/src/pages/backends/index.tsx @@ -17,7 +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 VersionInfoModal from './components/version-info-modal'; import { json2Yaml, yaml2Json } from './config'; import { FormData, ListItem } from './config/types'; import useExportYAML from './hooks/use-export-yaml'; @@ -54,11 +54,10 @@ const BackendList = () => { action: PageActionType; currentData?: Partial; }>({ open: false, action: 'create' }); - const [openViewBuiltinVersionsModal, setOpenViewBuiltinVersionsModal] = - useState<{ - open: boolean; - currentData?: Partial; - }>({ open: false }); + const [openVersionInfoModal, setOpenVersionInfoModal] = 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) => { @@ -133,10 +132,9 @@ const BackendList = () => { }); } else if (item.action === 'export') { // Export YAML action - console.log('Export YAML for:', item.data); exportYAML(item.data); } else if (item.action === 'view_versions') { - setOpenViewBuiltinVersionsModal({ + setOpenVersionInfoModal({ open: true, currentData: item.data }); @@ -156,13 +154,14 @@ const BackendList = () => { extra={[]} > { open={openModalStatus.open} title={ openModalStatus.action === 'create' - ? 'Create Backend' - : 'Edit Backend' + ? intl.formatMessage({ id: 'backend.button.add' }) + : intl.formatMessage({ id: 'backend.button.edit' }) } > - - setOpenViewBuiltinVersionsModal({ + setOpenVersionInfoModal({ open: false, currentData: undefined }) } - > + > ); diff --git a/src/pages/cluster-management/components/support-gpus.tsx b/src/pages/cluster-management/components/support-gpus.tsx index a0aa045f..a3439b21 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: 'Nvidia', + label: 'NVIDIA', value: 'cuda', description: '', key: 'cuda', diff --git a/src/pages/llmodels/apis/index.ts b/src/pages/llmodels/apis/index.ts index cf3f5af9..7131cacc 100644 --- a/src/pages/llmodels/apis/index.ts +++ b/src/pages/llmodels/apis/index.ts @@ -399,7 +399,7 @@ export async function evaluationsModelSpec( }; } -export async function queryBackendList() { +export async function queryBackendList(params?: { cluster_id: number }) { return request<{ items: { backend_name: string; @@ -410,6 +410,7 @@ export async function queryBackendList() { versions: string[]; }[]; }>(BACKEND_LIST_API, { - method: 'GET' + method: 'GET', + params }); } diff --git a/src/pages/llmodels/components/deploy-builtin-modal.tsx b/src/pages/llmodels/components/deploy-builtin-modal.tsx index d31f768b..1e8b091b 100644 --- a/src/pages/llmodels/components/deploy-builtin-modal.tsx +++ b/src/pages/llmodels/components/deploy-builtin-modal.tsx @@ -245,6 +245,7 @@ const AddModal: React.FC = (props) => { return result; }; + // TODO need check the backend is available const handleSetBackendOptions = () => { const backendGroup = _.groupBy(specListRef.current, 'backend'); @@ -495,6 +496,9 @@ const AddModal: React.FC = (props) => { form.current?.getGPUOptionList?.({ clusterId: initClusterId() }); + form.current?.getBackendOptions?.({ + cluster_id: initClusterId() + }); }, 100); } return () => { @@ -596,7 +600,6 @@ const AddModal: React.FC = (props) => { isGGUF={isGGUF} formKey={deployFormKeyMap.catalog} sourceDisable={false} - backendOptions={backendList} sourceList={sourceList} clusterList={clusterList} onBackendChange={handleBackendChange} diff --git a/src/pages/llmodels/components/deploy-modal.tsx b/src/pages/llmodels/components/deploy-modal.tsx index c1db2b83..51c98a0c 100644 --- a/src/pages/llmodels/components/deploy-modal.tsx +++ b/src/pages/llmodels/components/deploy-modal.tsx @@ -10,7 +10,7 @@ import styled from 'styled-components'; 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 { FormData, SourceType } from '../config/types'; import DataForm from '../forms'; import { MessageStatus, @@ -63,7 +63,6 @@ type AddModalProps = { width?: string | number; initialValues?: any; deploymentType?: 'modelList' | 'modelFiles'; - backendOptions: BackendOption[]; clusterList: Global.BaseOption< number, { provider: string; state: string | number } @@ -326,15 +325,17 @@ const AddModal: FC = (props) => { return; } if (data.local_path || props.source !== modelSourceMap.local_path_value) { + // TODO confirm wheather it is gguf by model file not by backend handleOnValuesChange?.({ changedValues: {}, - allValues: - backend === backendOptionsMap.llamaBox - ? data - : _.omit(data, [ - 'cpu_offloading', - 'distributed_inference_across_workers' - ]), + // allValues: + // backend === backendOptionsMap.llamaBox + // ? data + // : _.omit(data, [ + // 'cpu_offloading', + // 'distributed_inference_across_workers' + // ]), + allValues: data, source: props.source }); } @@ -413,6 +414,9 @@ const AddModal: FC = (props) => { form.current?.getGPUOptionList?.({ clusterId: initClusterId() }); + form.current?.getBackendOptions?.({ + cluster_id: initClusterId() + }); } else { cancelEvaluate(); clearCahceFormValues(); @@ -542,7 +546,6 @@ const AddModal: FC = (props) => { onOk={handleOnOk} ref={form} isGGUF={isGGUF} - backendOptions={props.backendOptions} onBackendChange={handleBackendChange} onValuesChange={onValuesChange} > diff --git a/src/pages/llmodels/components/table-list.tsx b/src/pages/llmodels/components/table-list.tsx index 7203d132..22dcb7c8 100644 --- a/src/pages/llmodels/components/table-list.tsx +++ b/src/pages/llmodels/components/table-list.tsx @@ -42,7 +42,6 @@ import { modelCategoriesMap, modelSourceMap } from '../config'; -import { backendOptionsMap } from '../config/backend-parameters'; import { ButtonList, categoryToPathMap, @@ -57,7 +56,6 @@ import { } from '../config/types'; import useFormInitialValues from '../hooks/use-form-initial-values'; import useModelsColumns from '../hooks/use-models-columns'; -import useQueryBackends from '../hooks/use-query-backends'; import APIAccessInfoModal from './api-access-info'; import DeployModal from './deploy-modal'; import Instances from './instances'; @@ -124,7 +122,6 @@ const Models: React.FC = ({ loadend, total }) => { - const { backendOptions } = useQueryBackends(); const { generateFormValues, clusterList, getClusterList } = useFormInitialValues(); const { saveScrollHeight, restoreScrollHeight } = useBodyScroll(); @@ -401,7 +398,7 @@ const Models: React.FC = ({ const initialValues = generateFormValues(row, []); setUpdateFormInitials({ data: initialValues, - isGGUF: row.backend === backendOptionsMap.llamaBox + isGGUF: false }); setCurrentData(row); setOpenAddModal(true); @@ -693,7 +690,6 @@ const Models: React.FC = ({ clusterList={clusterList} onCancel={handleModalCancel} onOk={handleModalOk} - backendOptions={backendOptions} > = ({ isGGUF={openDeployModal.isGGUF} hasLinuxWorker={openDeployModal.hasLinuxWorker} clusterList={clusterList} - backendOptions={backendOptions} onCancel={handleDeployModalCancel} onOk={handleCreateModel} > diff --git a/src/pages/llmodels/components/update-modal.tsx b/src/pages/llmodels/components/update-modal.tsx index 0c06f055..4d25e8a8 100644 --- a/src/pages/llmodels/components/update-modal.tsx +++ b/src/pages/llmodels/components/update-modal.tsx @@ -12,7 +12,7 @@ import { updateIgnoreFields } from '../config'; import { backendOptionsMap } from '../config/backend-parameters'; -import { BackendOption, FormData } from '../config/types'; +import { FormData } from '../config/types'; import { generateGPUSelector } from '../config/utils'; import DataForm from '../forms'; import { useCheckCompatibility } from '../hooks'; @@ -26,7 +26,6 @@ type AddModalProps = { data: FormData; isGGUF: boolean; }; - backendOptions: BackendOption[]; clusterList: Global.BaseOption< number, { provider: string; state: string | number } @@ -326,7 +325,6 @@ const UpdateModal: React.FC = (props) => { onOk={handleOk} ref={formRef} isGGUF={isGGUF} - backendOptions={props.backendOptions} onBackendChange={handleAsyncBackendChange} onValuesChange={handleManulOnValuesChange} > diff --git a/src/pages/llmodels/config/backend-parameters/index.ts b/src/pages/llmodels/config/backend-parameters/index.ts index 0e8be436..8c230a19 100644 --- a/src/pages/llmodels/config/backend-parameters/index.ts +++ b/src/pages/llmodels/config/backend-parameters/index.ts @@ -4,9 +4,9 @@ import vllmParameters from './vllm-parameters'; export const backendOptionsMap = { llamaBox: 'llama-box', - vllm: 'vllm', + vllm: 'vLLM', voxBox: 'vox-box', - ascendMindie: 'ascend-mindie', + ascendMindie: 'MindIE', custom: 'custom' }; diff --git a/src/pages/llmodels/forms/index.tsx b/src/pages/llmodels/forms/index.tsx index 1d212689..c0d26c02 100644 --- a/src/pages/llmodels/forms/index.tsx +++ b/src/pages/llmodels/forms/index.tsx @@ -22,6 +22,7 @@ import CatalogFrom from './catalog'; import LocalPathSource from './local-path-source'; import OnlineSource from './online-source'; // import AdvanceConfig from './advance-config'; +import useQueryBackends from '../hooks/use-query-backends'; import AdvanceConfig from './advance-config'; import Performance from './performance'; @@ -33,7 +34,6 @@ interface DataFormProps { isGGUF: boolean; formKey: DeployFormKey; sourceDisable?: boolean; - backendOptions: BackendOption[]; sourceList?: Global.BaseOption[]; clusterList: Global.BaseOption[]; fields?: string[]; @@ -50,7 +50,6 @@ const DataForm: React.FC = forwardRef((props, ref) => { formKey, initialValues, sourceDisable = true, - backendOptions = [], sourceList, clusterList = [], fields = ['source'], @@ -58,6 +57,7 @@ const DataForm: React.FC = forwardRef((props, ref) => { onValuesChange, onOk } = props; + const { backendOptions, getBackendOptions } = useQueryBackends(); const { getGPUOptionList, gpuOptions } = useGenerateGPUOptions(); const { getRuleMessage } = useAppUtils(); const [form] = Form.useForm(); @@ -111,6 +111,7 @@ const DataForm: React.FC = forwardRef((props, ref) => { const handleClusterChange = (value: number) => { getGPUOptionList({ clusterId: value }); + getBackendOptions({ cluster_id: value }); }; const handleOnValuesChange = async (changedValues: any, allValues: any) => { @@ -147,6 +148,9 @@ const DataForm: React.FC = forwardRef((props, ref) => { }, getGPUOptionList: async (params: { clusterId: number }) => { return await getGPUOptionList(params); + }, + getBackendOptions: async (params?: { cluster_id: number }) => { + getBackendOptions(params); } }; }); diff --git a/src/pages/llmodels/hooks/use-query-backends.ts b/src/pages/llmodels/hooks/use-query-backends.ts index 1cc3adda..b580775e 100644 --- a/src/pages/llmodels/hooks/use-query-backends.ts +++ b/src/pages/llmodels/hooks/use-query-backends.ts @@ -1,14 +1,13 @@ import { backendOptionsAtom } from '@/atoms/models'; import { useAtom } from 'jotai'; -import { useEffect } from 'react'; import { queryBackendList } from '../apis'; export default function useQueryBackends() { const [backendOptions, setBackendOptions] = useAtom(backendOptionsAtom); - const getBackendOptions = async () => { + const getBackendOptions = async (params?: { cluster_id: number }) => { try { - const res = await queryBackendList(); + const res = await queryBackendList(params); const list = res?.items?.map((item) => { return { value: item.backend_name, @@ -30,10 +29,6 @@ export default function useQueryBackends() { } }; - useEffect(() => { - getBackendOptions(); - }, []); - return { backendOptions, getBackendOptions diff --git a/src/pages/resources/components/model-files.tsx b/src/pages/resources/components/model-files.tsx index f7916359..1b8261b6 100644 --- a/src/pages/resources/components/model-files.tsx +++ b/src/pages/resources/components/model-files.tsx @@ -17,7 +17,6 @@ import { SourceType } from '@/pages/llmodels/config/types'; import DownloadModal from '@/pages/llmodels/download'; import useCheckBackend from '@/pages/llmodels/hooks/use-check-backend'; import { useGenerateWorkerOptions } from '@/pages/llmodels/hooks/use-form-initial-values'; -import useQueryBackends from '@/pages/llmodels/hooks/use-query-backends'; import useRecognizeAudio from '@/pages/llmodels/hooks/use-recognize-audio'; import { PageContainer } from '@ant-design/pro-components'; import { useIntl, useNavigate } from '@umijs/max'; @@ -67,7 +66,6 @@ const ModelFiles = () => { watch: true, contentForDelete: 'resources.modelfiles.modelfile' }); - const { backendOptions } = useQueryBackends(); const intl = useIntl(); const { showSuccess } = useAppUtils(); const [downloadModalStatus, setDownlaodMoalStatus] = useState<{ @@ -347,7 +345,6 @@ const ModelFiles = () => { workerOptions={readyWorkers} >