feat: backend entrypoint
This commit is contained in:
@@ -1,4 +1,8 @@
|
|||||||
import { WarningFilled } from '@ant-design/icons';
|
import {
|
||||||
|
CheckCircleFilled,
|
||||||
|
LoadingOutlined,
|
||||||
|
WarningFilled
|
||||||
|
} from '@ant-design/icons';
|
||||||
import { Typography } from 'antd';
|
import { Typography } from 'antd';
|
||||||
import { createStyles } from 'antd-style';
|
import { createStyles } from 'antd-style';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
@@ -122,6 +126,17 @@ const AlertInfo: React.FC<AlertInfoProps> = (props) => {
|
|||||||
overlayScrollerProps = {}
|
overlayScrollerProps = {}
|
||||||
} = props;
|
} = props;
|
||||||
const { styles } = useStyles();
|
const { styles } = useStyles();
|
||||||
|
|
||||||
|
const renderIcon = () => {
|
||||||
|
if (type === 'transition') {
|
||||||
|
return <LoadingOutlined />;
|
||||||
|
}
|
||||||
|
if (type === 'success') {
|
||||||
|
return <CheckCircleFilled />;
|
||||||
|
}
|
||||||
|
return <WarningFilled />;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{message ? (
|
{message ? (
|
||||||
@@ -139,7 +154,7 @@ const AlertInfo: React.FC<AlertInfoProps> = (props) => {
|
|||||||
>
|
>
|
||||||
<div className={classNames('title', type)}>
|
<div className={classNames('title', type)}>
|
||||||
<span className={classNames('info-icon', type)}>
|
<span className={classNames('info-icon', type)}>
|
||||||
{icon ?? <WarningFilled />}
|
{icon ?? renderIcon()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{title && (
|
{title && (
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ const SealTextArea: React.FC<InputTextareaProps & SealFormItemProps> = (
|
|||||||
<InputWrapper>
|
<InputWrapper>
|
||||||
<Wrapper
|
<Wrapper
|
||||||
status={status}
|
status={status}
|
||||||
label={<LabelWrapper>{label}</LabelWrapper>}
|
label={label && <LabelWrapper>{label}</LabelWrapper>}
|
||||||
isFocus={alwaysFocus || isFocus}
|
isFocus={alwaysFocus || isFocus}
|
||||||
required={required}
|
required={required}
|
||||||
description={description}
|
description={description}
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import { Switch, Tooltip } from 'antd';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import LabelInfo from '../seal-form/components/label-info';
|
||||||
|
|
||||||
|
const SwitchContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
border: 1px solid var(--ant-color-border);
|
||||||
|
border-radius: var(--ant-border-radius);
|
||||||
|
padding: 12px 14px;
|
||||||
|
min-height: 54px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const LabelContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface SwitchInputProps {
|
||||||
|
label?: React.ReactNode;
|
||||||
|
description?: string;
|
||||||
|
checked?: boolean;
|
||||||
|
defaultChecked?: boolean;
|
||||||
|
onChange?: (checked: boolean) => void;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
style?: React.CSSProperties;
|
||||||
|
alwaysShowChildren?: boolean;
|
||||||
|
btnTips?: React.ReactNode;
|
||||||
|
size?: 'small' | 'default';
|
||||||
|
}
|
||||||
|
|
||||||
|
const SwitchInput: React.FC<SwitchInputProps> = (props) => {
|
||||||
|
const {
|
||||||
|
label,
|
||||||
|
description,
|
||||||
|
checked,
|
||||||
|
defaultChecked,
|
||||||
|
onChange,
|
||||||
|
children,
|
||||||
|
style,
|
||||||
|
alwaysShowChildren = true,
|
||||||
|
size,
|
||||||
|
btnTips
|
||||||
|
} = props;
|
||||||
|
const [internalChecked, setInternalChecked] = useState<boolean>(
|
||||||
|
defaultChecked || false
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleChange = (checked: boolean) => {
|
||||||
|
setInternalChecked(checked);
|
||||||
|
onChange?.(checked);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SwitchContainer style={style}>
|
||||||
|
<LabelContainer>
|
||||||
|
<LabelInfo label={label} description={description} />
|
||||||
|
<Tooltip title={btnTips}>
|
||||||
|
<Switch
|
||||||
|
size={size}
|
||||||
|
checked={checked !== undefined ? checked : internalChecked}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</LabelContainer>
|
||||||
|
{(alwaysShowChildren || internalChecked) && children}
|
||||||
|
</SwitchContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SwitchInput;
|
||||||
+1
-1
@@ -203,7 +203,7 @@ body {
|
|||||||
|
|
||||||
// form item help
|
// form item help
|
||||||
.ant-form-item-with-help .ant-form-item-explain {
|
.ant-form-item-with-help .ant-form-item-explain {
|
||||||
padding-left: 16px;
|
// padding-left: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
// icon
|
// icon
|
||||||
|
|||||||
@@ -34,5 +34,9 @@ export default {
|
|||||||
'The custom backend name must end with "-custom".',
|
'The custom backend name must end with "-custom".',
|
||||||
'backend.quickConfig': 'Quick Config',
|
'backend.quickConfig': 'Quick Config',
|
||||||
'backend.version.default.not.exists':
|
'backend.version.default.not.exists':
|
||||||
'The default version does not exist in {versions}.'
|
'The default version does not exist in {versions}.',
|
||||||
|
'backend.replaceEntrypoint': 'Override Image Entrypoint',
|
||||||
|
'backend.entrypoint': 'Image Entrypoint',
|
||||||
|
'backend.entrypoint.tips':
|
||||||
|
'If specified, the ENTRYPOINT defined in the image will be ignored, and the command below will be used as the container startup entrypoint.'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -89,8 +89,10 @@ Same applies to the <span class="bold-text">/opt/dtk</span> directory.`,
|
|||||||
'clusters.button.genToken':
|
'clusters.button.genToken':
|
||||||
'Need to create a new token? Click <a href="{link}" target="_blank">here</a>.',
|
'Need to create a new token? Click <a href="{link}" target="_blank">here</a>.',
|
||||||
'clusters.addworker.amdNotes-01': `If the <span class="bold-text">/opt/rocm</span> directory does not exist, please create a symbolic link pointing to the ROCm installed path: <span class="bold-text">ln -s /path/to/rocm /opt/rocm</span>.`,
|
'clusters.addworker.amdNotes-01': `If the <span class="bold-text">/opt/rocm</span> directory does not exist, please create a symbolic link pointing to the ROCm installed path: <span class="bold-text">ln -s /path/to/rocm /opt/rocm</span>.`,
|
||||||
'clusters.addworker.message.success':
|
'clusters.addworker.message.success_single':
|
||||||
'{count} workers have been added to the cluster.',
|
'{count} new worker has been added to the cluster.',
|
||||||
|
'clusters.addworker.message.success_multiple':
|
||||||
|
'{count} new workers have been added to the cluster.',
|
||||||
'clusters.create.serverUrl': 'Server URL',
|
'clusters.create.serverUrl': 'Server URL',
|
||||||
'clusters.create.workerConfig': 'Worker Configuration',
|
'clusters.create.workerConfig': 'Worker Configuration',
|
||||||
'clusters.addworker.containerName': 'Worker Container Name',
|
'clusters.addworker.containerName': 'Worker Container Name',
|
||||||
|
|||||||
@@ -34,5 +34,9 @@ export default {
|
|||||||
'The custom backend name must end with "-custom".',
|
'The custom backend name must end with "-custom".',
|
||||||
'backend.quickConfig': 'Quick Config',
|
'backend.quickConfig': 'Quick Config',
|
||||||
'backend.version.default.not.exists':
|
'backend.version.default.not.exists':
|
||||||
'The default version does not exist in {versions}.'
|
'The default version does not exist in {versions}.',
|
||||||
|
'backend.replaceEntrypoint': 'Override Image Entrypoint',
|
||||||
|
'backend.entrypoint': 'Image Entrypoint',
|
||||||
|
'backend.entrypoint.tips':
|
||||||
|
'If specified, the ENTRYPOINT defined in the image will be ignored, and the command below will be used as the container startup entrypoint.'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -89,8 +89,10 @@ Same applies to the <span class="bold-text">/opt/dtk</span> directory.`,
|
|||||||
'clusters.button.genToken':
|
'clusters.button.genToken':
|
||||||
'Need to create a new token? Click <a href="{link}" target="_blank">here</a>.',
|
'Need to create a new token? Click <a href="{link}" target="_blank">here</a>.',
|
||||||
'clusters.addworker.amdNotes-01': `If the <span class="bold-text">/opt/rocm</span> directory does not exist, please create a symbolic link pointing to the ROCm installed path: <span class="bold-text">ln -s /path/to/rocm /opt/rocm</span>.`,
|
'clusters.addworker.amdNotes-01': `If the <span class="bold-text">/opt/rocm</span> directory does not exist, please create a symbolic link pointing to the ROCm installed path: <span class="bold-text">ln -s /path/to/rocm /opt/rocm</span>.`,
|
||||||
'clusters.addworker.message.success':
|
'clusters.addworker.message.success_single':
|
||||||
'{count} workers have been added to the cluster.',
|
'{count} new worker has been added to the cluster.',
|
||||||
|
'clusters.addworker.message.success_multiple':
|
||||||
|
'{count} new workers have been added to the cluster.',
|
||||||
'clusters.create.serverUrl': 'Server URL',
|
'clusters.create.serverUrl': 'Server URL',
|
||||||
'clusters.create.workerConfig': 'Worker Configuration',
|
'clusters.create.workerConfig': 'Worker Configuration',
|
||||||
'clusters.addworker.containerName': 'Worker Container Name',
|
'clusters.addworker.containerName': 'Worker Container Name',
|
||||||
@@ -179,11 +181,12 @@ Same applies to the <span class="bold-text">/opt/dtk</span> directory.`,
|
|||||||
// 71. 'clusters.addworker.cacheVolume': 'Model Cache Volume Mount',
|
// 71. 'clusters.addworker.cacheVolume': 'Model Cache Volume Mount',
|
||||||
// 72. 'clusters.addworker.cacheVolume.tips': 'If you want to customize the model cache directory, you can specify the path to mount it.',
|
// 72. 'clusters.addworker.cacheVolume.tips': 'If you want to customize the model cache directory, you can specify the path to mount it.',
|
||||||
// 73. 'clusters.addworker.cacheVolume.holder': 'e.g. /data/cache (path must start with /)',
|
// 73. 'clusters.addworker.cacheVolume.holder': 'e.g. /data/cache (path must start with /)',
|
||||||
// 74. 'clusters.addworker.message.success': '{count} workers have been added to the cluster.',
|
// 74. 'clusters.addworker.message.success_single': '{count} new worker has been added to the cluster.',
|
||||||
// 75. 'clusters.create.serverUrl': 'Server URL',
|
// 75. 'clusters.addworker.message.success_multiple': '{count} new workers have been added to the cluster.',
|
||||||
// 76. 'clusters.create.workerConfig': 'Worker Configuration'
|
// 76. 'clusters.create.serverUrl': 'Server URL',
|
||||||
// 75. 'clusters.addworker.containerName': 'Worker Container Name',
|
// 77. 'clusters.create.workerConfig': 'Worker Configuration'
|
||||||
// 76. 'clusters.addworker.containerName.tips':'Specify a name for the worker container.',
|
// 78. 'clusters.addworker.containerName': 'Worker Container Name',
|
||||||
|
// 79. 'clusters.addworker.containerName.tips':'Specify a name for the worker container.',
|
||||||
// 77. 'clusters.addworker.dataVolume': 'GPUStack Data Volume',
|
// 77. 'clusters.addworker.dataVolume': 'GPUStack Data Volume',
|
||||||
// 78. 'clusters.addworker.dataVolume.tips': 'Specify a data storage path for GPUStack.',
|
// 78. 'clusters.addworker.dataVolume.tips': 'Specify a data storage path for GPUStack.',
|
||||||
// 79. 'clusters.table.ip.internal': 'Internal',
|
// 79. 'clusters.table.ip.internal': 'Internal',
|
||||||
|
|||||||
@@ -34,9 +34,17 @@ export default {
|
|||||||
'Пользовательское название бэкенда должно оканчиваться на «-custom».',
|
'Пользовательское название бэкенда должно оканчиваться на «-custom».',
|
||||||
'backend.quickConfig': 'Быстрая настройка',
|
'backend.quickConfig': 'Быстрая настройка',
|
||||||
'backend.version.default.not.exists':
|
'backend.version.default.not.exists':
|
||||||
'The default version does not exist in {versions}.'
|
'The default version does not exist in {versions}.',
|
||||||
|
'backend.replaceEntrypoint': 'Override Image Entrypoint',
|
||||||
|
'backend.entrypoint': 'Image Entrypoint',
|
||||||
|
'backend.entrypoint.tips':
|
||||||
|
'If specified, the ENTRYPOINT defined in the image will be ignored, and the command below will be used as the container startup entrypoint.'
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
// 1. 'backend.version.default.not.exists': 'The default version does not exist in {versions}.'
|
// 1. 'backend.version.default.not.exists': 'The default version does not exist in {versions}.',
|
||||||
|
// 2. 'backend.replaceEntrypoint': 'Override Image Entrypoint',
|
||||||
|
// 3. 'backend.entrypoint': 'Image Entrypoint',
|
||||||
|
// 4. 'backend.entrypoint.tips':
|
||||||
|
// 'If specified, the ENTRYPOINT defined in the image will be ignored, and the command below will be used as the container startup entrypoint.'
|
||||||
// ========== End of To-Do List ==========
|
// ========== End of To-Do List ==========
|
||||||
|
|||||||
@@ -89,8 +89,10 @@ export default {
|
|||||||
'clusters.button.genToken':
|
'clusters.button.genToken':
|
||||||
'Need to create a new token? Click <a href="{link}" target="_blank">here</a>.',
|
'Need to create a new token? Click <a href="{link}" target="_blank">here</a>.',
|
||||||
'clusters.addworker.amdNotes-01': `If the <span class="bold-text">/opt/rocm</span> directory does not exist, please create a symbolic link pointing to the ROCm installed path: <span class="bold-text">ln -s /path/to/rocm /opt/rocm</span>.`,
|
'clusters.addworker.amdNotes-01': `If the <span class="bold-text">/opt/rocm</span> directory does not exist, please create a symbolic link pointing to the ROCm installed path: <span class="bold-text">ln -s /path/to/rocm /opt/rocm</span>.`,
|
||||||
'clusters.addworker.message.success':
|
'clusters.addworker.message.success_single':
|
||||||
'{count} workers have been added to the cluster.',
|
'{count} new worker has been added to the cluster.',
|
||||||
|
'clusters.addworker.message.success_multiple':
|
||||||
|
'{count} new workers have been added to the cluster.',
|
||||||
'clusters.create.serverUrl': 'Server URL',
|
'clusters.create.serverUrl': 'Server URL',
|
||||||
'clusters.create.workerConfig': 'Worker Configuration',
|
'clusters.create.workerConfig': 'Worker Configuration',
|
||||||
'clusters.addworker.containerName': 'Worker Container Name',
|
'clusters.addworker.containerName': 'Worker Container Name',
|
||||||
@@ -110,13 +112,14 @@ export default {
|
|||||||
// 2. 'clusters.button.genToken': 'Need to create a new token? Click <a href="{link}" target="_blank">here</a>.',
|
// 2. 'clusters.button.genToken': 'Need to create a new token? Click <a href="{link}" target="_blank">here</a>.',
|
||||||
// 3. 'clusters.addworker.cacheVolume': 'Model Cache Volume Mount',
|
// 3. 'clusters.addworker.cacheVolume': 'Model Cache Volume Mount',
|
||||||
// 4. 'clusters.addworker.cacheVolume.tips': 'If you want to customize the model cache directory, you can specify the path to mount it.',
|
// 4. 'clusters.addworker.cacheVolume.tips': 'If you want to customize the model cache directory, you can specify the path to mount it.',
|
||||||
// 5. 'clusters.addworker.message.success': '{count} workers have been added to the cluster.',
|
// 5. 'clusters.addworker.message.success_single': '{count} new worker has been added to the cluster.',
|
||||||
// 6. 'clusters.create.serverUrl': 'Server URL',
|
// 6. 'clusters.addworker.message.success_multiple': '{count} new workers have been added to the cluster.',
|
||||||
// 7. 'clusters.create.workerConfig': 'Worker Configuration'
|
// 7. 'clusters.create.serverUrl': 'Server URL',
|
||||||
// 6. 'clusters.addworker.containerName': 'Worker Container Name',
|
// 8. 'clusters.create.workerConfig': 'Worker Configuration'
|
||||||
// 7. 'clusters.addworker.containerName.tips':'Specify a name for the worker container.',
|
// 9. 'clusters.addworker.containerName': 'Worker Container Name',
|
||||||
// 8. 'clusters.addworker.dataVolume': 'GPUStack Data Volume',
|
// 10. 'clusters.addworker.containerName.tips':'Specify a name for the worker container.',
|
||||||
// 9. 'clusters.addworker.dataVolume.tips': 'Specify a data storage path for GPUStack.',
|
// 11. 'clusters.addworker.dataVolume': 'GPUStack Data Volume',
|
||||||
|
// 12. 'clusters.addworker.dataVolume.tips': 'Specify a data storage path for GPUStack.',
|
||||||
// 10. 'clusters.table.ip.internal': 'Internal',
|
// 10. 'clusters.table.ip.internal': 'Internal',
|
||||||
// 11. 'clusters.table.ip.external': 'External',
|
// 11. 'clusters.table.ip.external': 'External',
|
||||||
// 12. 'clusters.form.serverUrl.tips': 'Specify the server URL accessible from your cloud provider.'
|
// 12. 'clusters.form.serverUrl.tips': 'Specify the server URL accessible from your cloud provider.'
|
||||||
|
|||||||
@@ -31,5 +31,9 @@ export default {
|
|||||||
'backend.version.no.tips': '内置后端自定义版本名称必须以 "-custom" 结尾。',
|
'backend.version.no.tips': '内置后端自定义版本名称必须以 "-custom" 结尾。',
|
||||||
'backend.backend.rules.custom': '自定义后端名称须以 "-custom" 结尾。',
|
'backend.backend.rules.custom': '自定义后端名称须以 "-custom" 结尾。',
|
||||||
'backend.quickConfig': '快速配置',
|
'backend.quickConfig': '快速配置',
|
||||||
'backend.version.default.not.exists': '默认版本不存在于 {versions}。'
|
'backend.version.default.not.exists': '默认版本不存在于 {versions}。',
|
||||||
|
'backend.replaceEntrypoint': '覆盖镜像入口命令',
|
||||||
|
'backend.entrypoint': '镜像入口命令',
|
||||||
|
'backend.entrypoint.tips':
|
||||||
|
'如指定,镜像中定义的 ENTRYPOINT 将被忽略,下面的命令将作为容器启动入口命令使用。'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -88,7 +88,10 @@ export default {
|
|||||||
'需要创建令牌?点击<a href="{link}" target="_blank">这里</a>。',
|
'需要创建令牌?点击<a href="{link}" target="_blank">这里</a>。',
|
||||||
'clusters.addworker.amdNotes-01':
|
'clusters.addworker.amdNotes-01':
|
||||||
'如果 <span class="bold-text">/opt/rocm</span> 目录不存在,请创建一个指向已安装 ROCm 路径的符号链接:<span class="bold-text">ln -s /path/to/rocm /opt/rocm</span>。',
|
'如果 <span class="bold-text">/opt/rocm</span> 目录不存在,请创建一个指向已安装 ROCm 路径的符号链接:<span class="bold-text">ln -s /path/to/rocm /opt/rocm</span>。',
|
||||||
'clusters.addworker.message.success': '已将 {count} 个工作节点添加到集群中。',
|
'clusters.addworker.message.success_single':
|
||||||
|
'已将 {count} 个新节点添加到集群中。',
|
||||||
|
'clusters.addworker.message.success_multiple':
|
||||||
|
'已将 {count} 个新节点添加到集群中。',
|
||||||
'clusters.create.serverUrl': '服务器地址',
|
'clusters.create.serverUrl': '服务器地址',
|
||||||
'clusters.create.workerConfig': '节点配置',
|
'clusters.create.workerConfig': '节点配置',
|
||||||
'clusters.addworker.containerName': '节点容器名称',
|
'clusters.addworker.containerName': '节点容器名称',
|
||||||
|
|||||||
@@ -84,7 +84,8 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
|||||||
acc[curr.version_no] = {
|
acc[curr.version_no] = {
|
||||||
custom_framework: curr.custom_framework,
|
custom_framework: curr.custom_framework,
|
||||||
image_name: curr.image_name,
|
image_name: curr.image_name,
|
||||||
run_command: curr.run_command
|
run_command: curr.run_command,
|
||||||
|
entrypoint: curr.entrypoint
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export interface VersionConfigs {
|
|||||||
is_default: boolean;
|
is_default: boolean;
|
||||||
built_in_frameworks?: string[];
|
built_in_frameworks?: string[];
|
||||||
custom_framework?: string;
|
custom_framework?: string;
|
||||||
|
entrypoint?: string;
|
||||||
version_no?: string;
|
version_no?: string;
|
||||||
is_built_in?: boolean;
|
is_built_in?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import CollapsibleContainer from '@/components/collapse-container';
|
|||||||
import BaseSelect from '@/components/seal-form/base/select';
|
import BaseSelect from '@/components/seal-form/base/select';
|
||||||
import SealInput from '@/components/seal-form/seal-input';
|
import SealInput from '@/components/seal-form/seal-input';
|
||||||
import SealSelect from '@/components/seal-form/seal-select';
|
import SealSelect from '@/components/seal-form/seal-select';
|
||||||
|
import SealTextArea from '@/components/seal-form/seal-textarea';
|
||||||
import { PageActionType } from '@/config/types';
|
import { PageActionType } from '@/config/types';
|
||||||
import useAppUtils from '@/hooks/use-app-utils';
|
import useAppUtils from '@/hooks/use-app-utils';
|
||||||
import { MinusOutlined, PlusOutlined } from '@ant-design/icons';
|
import { MinusOutlined, PlusOutlined } from '@ant-design/icons';
|
||||||
@@ -342,14 +343,36 @@ const VersionsForm: React.FC<AddModalProps> = ({
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item name={[name, 'entrypoint']}>
|
||||||
|
<SealInput.TextArea
|
||||||
|
allowClear
|
||||||
|
description={intl.formatMessage({
|
||||||
|
id: 'backend.entrypoint.tips'
|
||||||
|
})}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'backend.replaceEntrypoint'
|
||||||
|
})}
|
||||||
|
></SealInput.TextArea>
|
||||||
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={[name, 'run_command']}
|
name={[name, 'run_command']}
|
||||||
style={{ marginBottom: 0 }}
|
style={{ marginBottom: 0 }}
|
||||||
>
|
>
|
||||||
<SealInput.TextArea
|
<SealTextArea
|
||||||
allowClear
|
allowClear
|
||||||
|
alwaysFocus={true}
|
||||||
|
description={intl.formatMessage({
|
||||||
|
id: 'backend.form.defaultExecuteCommand.tips'
|
||||||
|
})}
|
||||||
|
placeholder={intl.formatMessage(
|
||||||
|
{ id: 'common.help.eg' },
|
||||||
|
{
|
||||||
|
content:
|
||||||
|
'vllm serve {{model_path}} --port {{port}} --host {{worker_ip}} --served-model-name {{model_name}}'
|
||||||
|
}
|
||||||
|
)}
|
||||||
label={intl.formatMessage({ id: 'backend.runCommand' })}
|
label={intl.formatMessage({ id: 'backend.runCommand' })}
|
||||||
></SealInput.TextArea>
|
></SealTextArea>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</CollapsibleContainer>
|
</CollapsibleContainer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ interface AddWorkerContextProps {
|
|||||||
clusterLoading?: boolean;
|
clusterLoading?: boolean;
|
||||||
provider: ProviderType;
|
provider: ProviderType;
|
||||||
stepList: string[];
|
stepList: string[];
|
||||||
|
actionSource?: 'modal' | 'page';
|
||||||
|
onCancel?: () => void;
|
||||||
onClusterChange?: (value: number, row?: any) => void;
|
onClusterChange?: (value: number, row?: any) => void;
|
||||||
collapseKey: Set<string>;
|
collapseKey: Set<string>;
|
||||||
onToggle: (open: boolean, key: string) => void;
|
onToggle: (open: boolean, key: string) => void;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import AlertInfoBlock from '@/components/alert-info/block';
|
import AlertBlockInfo from '@/components/alert-info/block';
|
||||||
import useAddWorkerMessage from '@/pages/cluster-management/hooks/use-add-worker-message';
|
import useAddWorkerMessage from '@/pages/cluster-management/hooks/use-add-worker-message';
|
||||||
import { ExclamationCircleFilled } from '@ant-design/icons';
|
import { ExclamationCircleFilled } from '@ant-design/icons';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
@@ -36,6 +36,7 @@ type AddWorkerProps = {
|
|||||||
clusterLoading?: boolean;
|
clusterLoading?: boolean;
|
||||||
stepList: StepName[];
|
stepList: StepName[];
|
||||||
onClusterChange?: (value: number, row?: any) => void;
|
onClusterChange?: (value: number, row?: any) => void;
|
||||||
|
onCancel?: () => void;
|
||||||
registrationInfo: {
|
registrationInfo: {
|
||||||
token: string;
|
token: string;
|
||||||
image: string;
|
image: string;
|
||||||
@@ -57,6 +58,7 @@ const AddWorkerSteps: React.FC<AddWorkerProps> = (props) => {
|
|||||||
clusterList,
|
clusterList,
|
||||||
clusterLoading,
|
clusterLoading,
|
||||||
stepList = [],
|
stepList = [],
|
||||||
|
onCancel,
|
||||||
onClusterChange
|
onClusterChange
|
||||||
} = props || {};
|
} = props || {};
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
@@ -82,12 +84,28 @@ const AddWorkerSteps: React.FC<AddWorkerProps> = (props) => {
|
|||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
// this effect is only triggered when used in cluster create page
|
// this effect is only triggered when used in cluster create page
|
||||||
console.log('actionSource:', actionSource);
|
|
||||||
if (actionSource === 'page') {
|
if (actionSource === 'page') {
|
||||||
createModelsChunkRequest();
|
createModelsChunkRequest();
|
||||||
}
|
}
|
||||||
}, [actionSource]);
|
}, [actionSource]);
|
||||||
|
|
||||||
|
const renderMessage = (count: number) => {
|
||||||
|
if (count === 1) {
|
||||||
|
return intl.formatMessage(
|
||||||
|
{
|
||||||
|
id: 'clusters.addworker.message.success_single'
|
||||||
|
},
|
||||||
|
{ count: addedCount }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return intl.formatMessage(
|
||||||
|
{
|
||||||
|
id: 'clusters.addworker.message.success_multiple'
|
||||||
|
},
|
||||||
|
{ count: addedCount }
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AddWorkerContext.Provider
|
<AddWorkerContext.Provider
|
||||||
value={{
|
value={{
|
||||||
@@ -96,7 +114,9 @@ const AddWorkerSteps: React.FC<AddWorkerProps> = (props) => {
|
|||||||
provider,
|
provider,
|
||||||
stepList: stepList,
|
stepList: stepList,
|
||||||
collapseKey,
|
collapseKey,
|
||||||
|
actionSource,
|
||||||
onToggle,
|
onToggle,
|
||||||
|
onCancel,
|
||||||
onClusterChange: handleOnClusterChange,
|
onClusterChange: handleOnClusterChange,
|
||||||
registrationInfo,
|
registrationInfo,
|
||||||
summary,
|
summary,
|
||||||
@@ -110,7 +130,7 @@ const AddWorkerSteps: React.FC<AddWorkerProps> = (props) => {
|
|||||||
)}
|
)}
|
||||||
{stepList.includes(StepNamesMap.SelectCluster) &&
|
{stepList.includes(StepNamesMap.SelectCluster) &&
|
||||||
!clusterList?.length && (
|
!clusterList?.length && (
|
||||||
<AlertInfoBlock
|
<AlertBlockInfo
|
||||||
maxHeight={200}
|
maxHeight={200}
|
||||||
style={{ marginBottom: 8 }}
|
style={{ marginBottom: 8 }}
|
||||||
type="warning"
|
type="warning"
|
||||||
@@ -118,7 +138,7 @@ const AddWorkerSteps: React.FC<AddWorkerProps> = (props) => {
|
|||||||
message={intl.formatMessage({
|
message={intl.formatMessage({
|
||||||
id: 'resources.worker.noCluster.tips'
|
id: 'resources.worker.noCluster.tips'
|
||||||
})}
|
})}
|
||||||
></AlertInfoBlock>
|
></AlertBlockInfo>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* render the steps only when there is at least one cluster available or cluster selection is not required */}
|
{/* render the steps only when there is at least one cluster available or cluster selection is not required */}
|
||||||
@@ -142,14 +162,14 @@ const AddWorkerSteps: React.FC<AddWorkerProps> = (props) => {
|
|||||||
)}
|
)}
|
||||||
{addedCount > 0 && (
|
{addedCount > 0 && (
|
||||||
<Alert
|
<Alert
|
||||||
style={{ width: 'max-content' }}
|
style={{
|
||||||
message={intl.formatMessage(
|
textAlign: 'left',
|
||||||
{
|
borderColor: 'var(--ant-color-success)',
|
||||||
id: 'clusters.addworker.message.success'
|
width: '100%'
|
||||||
},
|
}}
|
||||||
{ count: addedCount }
|
|
||||||
)}
|
|
||||||
type="success"
|
type="success"
|
||||||
|
message={renderMessage(addedCount)}
|
||||||
|
closable
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import ScrollerModal from '@/components/scroller-modal';
|
import ScrollerModal from '@/components/scroller-modal';
|
||||||
import useAddWorkerMessage from '@/pages/cluster-management/hooks/use-add-worker-message';
|
import useAddWorkerMessage from '@/pages/cluster-management/hooks/use-add-worker-message';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Alert, Button } from 'antd';
|
import { Alert } from 'antd';
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { queryClusterToken } from '../../apis';
|
import { queryClusterToken } from '../../apis';
|
||||||
@@ -100,6 +100,23 @@ const AddWorker: React.FC<AddWorkerProps> = (props) => {
|
|||||||
}
|
}
|
||||||
}, [open]);
|
}, [open]);
|
||||||
|
|
||||||
|
const renderMessage = (count: number) => {
|
||||||
|
if (count === 1) {
|
||||||
|
return intl.formatMessage(
|
||||||
|
{
|
||||||
|
id: 'clusters.addworker.message.success_single'
|
||||||
|
},
|
||||||
|
{ count: addedCount }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return intl.formatMessage(
|
||||||
|
{
|
||||||
|
id: 'clusters.addworker.message.success_multiple'
|
||||||
|
},
|
||||||
|
{ count: addedCount }
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollerModal
|
<ScrollerModal
|
||||||
title={title}
|
title={title}
|
||||||
@@ -115,32 +132,23 @@ const AddWorker: React.FC<AddWorkerProps> = (props) => {
|
|||||||
maxContentHeight={'max(calc(100vh - 200px), 600px)'}
|
maxContentHeight={'max(calc(100vh - 200px), 600px)'}
|
||||||
footer={
|
footer={
|
||||||
<Footer>
|
<Footer>
|
||||||
<span className="tips">
|
{addedCount > 0 && (
|
||||||
{addedCount > 0 && (
|
<Alert
|
||||||
<Alert
|
style={{
|
||||||
message={intl.formatMessage(
|
textAlign: 'left',
|
||||||
{
|
borderColor: 'var(--ant-color-success)',
|
||||||
id: 'clusters.addworker.message.success'
|
width: '100%'
|
||||||
},
|
}}
|
||||||
{ count: addedCount }
|
type="success"
|
||||||
)}
|
message={renderMessage(addedCount)}
|
||||||
type="success"
|
closable
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</span>
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
onClick={onCancel}
|
|
||||||
style={{
|
|
||||||
minWidth: 88
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{intl.formatMessage({ id: 'common.button.done' })}
|
|
||||||
</Button>
|
|
||||||
</Footer>
|
</Footer>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<AddWorkerStep
|
<AddWorkerStep
|
||||||
|
onCancel={onCancel}
|
||||||
actionSource={'modal'}
|
actionSource={'modal'}
|
||||||
stepList={stepList}
|
stepList={stepList}
|
||||||
provider={provider}
|
provider={provider}
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ const Box = styled.div`
|
|||||||
const ButtonWrapper = styled.div`
|
const ButtonWrapper = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
`;
|
`;
|
||||||
@@ -41,7 +43,8 @@ const StepCollapse: React.FC<StepItemProps> = ({
|
|||||||
...rest
|
...rest
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { collapseKey, onToggle, stepList } = useAddWorkerContext();
|
const { collapseKey, onToggle, stepList, actionSource, onCancel } =
|
||||||
|
useAddWorkerContext();
|
||||||
|
|
||||||
const handleOnNext = async () => {
|
const handleOnNext = async () => {
|
||||||
const res = await beforeNext?.();
|
const res = await beforeNext?.();
|
||||||
@@ -51,8 +54,16 @@ const StepCollapse: React.FC<StepItemProps> = ({
|
|||||||
onToggle(true, nextName);
|
onToggle(true, nextName);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleOnPrevious = () => {
|
||||||
|
// find the previous step and open it
|
||||||
|
const previousName = stepList[stepList.indexOf(name) - 1];
|
||||||
|
onToggle(true, previousName);
|
||||||
|
};
|
||||||
|
|
||||||
const isLastStep = stepList.indexOf(name) === stepList.length - 1;
|
const isLastStep = stepList.indexOf(name) === stepList.length - 1;
|
||||||
|
|
||||||
|
const isFirstStep = stepList.indexOf(name) === 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
className={
|
className={
|
||||||
@@ -75,13 +86,31 @@ const StepCollapse: React.FC<StepItemProps> = ({
|
|||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
{!isLastStep && (
|
|
||||||
<ButtonWrapper>
|
<ButtonWrapper>
|
||||||
|
{!isFirstStep && (
|
||||||
|
<Button onClick={handleOnPrevious}>
|
||||||
|
{intl.formatMessage({ id: 'common.button.prev' })}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isLastStep && (
|
||||||
<Button type="primary" onClick={handleOnNext}>
|
<Button type="primary" onClick={handleOnNext}>
|
||||||
{intl.formatMessage({ id: 'common.button.next' })}
|
{intl.formatMessage({ id: 'common.button.next' })}
|
||||||
</Button>
|
</Button>
|
||||||
</ButtonWrapper>
|
)}
|
||||||
)}
|
{isLastStep && actionSource === 'modal' && (
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
onClick={onCancel}
|
||||||
|
style={{
|
||||||
|
minWidth: 88
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{intl.formatMessage({ id: 'common.button.done' })}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</ButtonWrapper>
|
||||||
</CollapsibleContainer>
|
</CollapsibleContainer>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const ClusterAdvanceConfig: React.FC<{
|
|||||||
return editorRef.current?.getValue();
|
return editorRef.current?.getValue();
|
||||||
},
|
},
|
||||||
setYamlValue: (values: any) => {
|
setYamlValue: (values: any) => {
|
||||||
editorRef.current?.setValue(values);
|
editorRef.current?.setValue(values || yamlTemplate);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
import AlertBlockInfo from '@/components/alert-info/block';
|
import AlertBlockInfo from '@/components/alert-info/block';
|
||||||
import {
|
import { CloseOutlined } from '@ant-design/icons';
|
||||||
CheckCircleFilled,
|
|
||||||
CloseOutlined,
|
|
||||||
LoadingOutlined,
|
|
||||||
WarningFilled
|
|
||||||
} from '@ant-design/icons';
|
|
||||||
import { Button } from 'antd';
|
import { Button } from 'antd';
|
||||||
import { isArray } from 'lodash';
|
import { isArray } from 'lodash';
|
||||||
import React, { useCallback, useMemo } from 'react';
|
import React, { useCallback, useMemo } from 'react';
|
||||||
@@ -98,16 +93,6 @@ const CompatibilityAlert: React.FC<CompatibilityAlertProps> = (props) => {
|
|||||||
return '';
|
return '';
|
||||||
}, [message, show, handleLinkMessage]);
|
}, [message, show, handleLinkMessage]);
|
||||||
|
|
||||||
const renderIcon = useMemo(() => {
|
|
||||||
if (type === 'transition') {
|
|
||||||
return <LoadingOutlined />;
|
|
||||||
}
|
|
||||||
if (type === 'success') {
|
|
||||||
return <CheckCircleFilled />;
|
|
||||||
}
|
|
||||||
return <WarningFilled />;
|
|
||||||
}, [type]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
show &&
|
show &&
|
||||||
!!message && (
|
!!message && (
|
||||||
@@ -118,7 +103,6 @@ const CompatibilityAlert: React.FC<CompatibilityAlertProps> = (props) => {
|
|||||||
title={title}
|
title={title}
|
||||||
contentStyle={contentStyle}
|
contentStyle={contentStyle}
|
||||||
type={type || 'warning'}
|
type={type || 'warning'}
|
||||||
icon={renderIcon}
|
|
||||||
style={{
|
style={{
|
||||||
paddingInlineEnd: showClose ? 20 : 16
|
paddingInlineEnd: showClose ? 20 : 16
|
||||||
}}
|
}}
|
||||||
|
|||||||
Reference in New Issue
Block a user