feat: model cache volume mount, clear gpu selector
This commit is contained in:
@@ -73,6 +73,9 @@ Same applies to the <span class="bold-text">/opt/dtk</span> directory.`,
|
|||||||
'clusters.addworker.enterWorkerIP': 'Enter worker IP',
|
'clusters.addworker.enterWorkerIP': 'Enter worker IP',
|
||||||
'clusters.addworker.enterWorkerIP.error': 'Please enter the worker IP.',
|
'clusters.addworker.enterWorkerIP.error': 'Please enter the worker IP.',
|
||||||
'clusters.addworker.extraVolume': 'Additional Volume Mount',
|
'clusters.addworker.extraVolume': 'Additional Volume Mount',
|
||||||
|
'clusters.addworker.cacheVolume': 'Model Cache Volume Mount',
|
||||||
|
'clusters.addworker.cacheVolume.tips':
|
||||||
|
'If you want to custom the model cache directory, you can specify the path to mount it.',
|
||||||
'clusters.addworker.configSummary': 'Configuration Summary',
|
'clusters.addworker.configSummary': 'Configuration Summary',
|
||||||
'clusters.addworker.gpuVendor': 'GPU Vendor',
|
'clusters.addworker.gpuVendor': 'GPU Vendor',
|
||||||
'clusters.addworker.workerIP': 'Worker IP',
|
'clusters.addworker.workerIP': 'Worker IP',
|
||||||
@@ -80,6 +83,8 @@ Same applies to the <span class="bold-text">/opt/dtk</span> directory.`,
|
|||||||
'clusters.addworker.autoDetect': 'Auto-detect',
|
'clusters.addworker.autoDetect': 'Auto-detect',
|
||||||
'clusters.addworker.extraVolume.holder':
|
'clusters.addworker.extraVolume.holder':
|
||||||
'e.g. /data/models (path must start with /)',
|
'e.g. /data/models (path must start with /)',
|
||||||
|
'clusters.addworker.cacheVolume.holder':
|
||||||
|
'e.g. /data/cache (path must start with /)',
|
||||||
'clusters.addworker.vendorNotes.title': 'Notes for {vendor} Device',
|
'clusters.addworker.vendorNotes.title': 'Notes for {vendor} Device',
|
||||||
'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>.',
|
||||||
|
|||||||
@@ -73,6 +73,9 @@ Same applies to the <span class="bold-text">/opt/dtk</span> directory.`,
|
|||||||
'clusters.addworker.enterWorkerIP': 'Enter worker IP',
|
'clusters.addworker.enterWorkerIP': 'Enter worker IP',
|
||||||
'clusters.addworker.enterWorkerIP.error': 'Please enter the worker IP.',
|
'clusters.addworker.enterWorkerIP.error': 'Please enter the worker IP.',
|
||||||
'clusters.addworker.extraVolume': 'Additional Volume Mount',
|
'clusters.addworker.extraVolume': 'Additional Volume Mount',
|
||||||
|
'clusters.addworker.cacheVolume': 'Model Cache Volume Mount',
|
||||||
|
'clusters.addworker.cacheVolume.tips':
|
||||||
|
'If you want to custom the model cache directory, you can specify the path to mount it.',
|
||||||
'clusters.addworker.configSummary': 'Configuration Summary',
|
'clusters.addworker.configSummary': 'Configuration Summary',
|
||||||
'clusters.addworker.gpuVendor': 'GPU Vendor',
|
'clusters.addworker.gpuVendor': 'GPU Vendor',
|
||||||
'clusters.addworker.workerIP': 'Worker IP',
|
'clusters.addworker.workerIP': 'Worker IP',
|
||||||
@@ -80,6 +83,8 @@ Same applies to the <span class="bold-text">/opt/dtk</span> directory.`,
|
|||||||
'clusters.addworker.autoDetect': 'Auto-detect',
|
'clusters.addworker.autoDetect': 'Auto-detect',
|
||||||
'clusters.addworker.extraVolume.holder':
|
'clusters.addworker.extraVolume.holder':
|
||||||
'e.g. /data/models (path must start with /)',
|
'e.g. /data/models (path must start with /)',
|
||||||
|
'clusters.addworker.cacheVolume.holder':
|
||||||
|
'e.g. /data/cache (path must start with /)',
|
||||||
'clusters.addworker.vendorNotes.title': 'Notes for {vendor} Device',
|
'clusters.addworker.vendorNotes.title': 'Notes for {vendor} Device',
|
||||||
'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>.',
|
||||||
@@ -156,5 +161,8 @@ Same applies to the <span class="bold-text">/opt/dtk</span> directory.`,
|
|||||||
// 67. 'clusters.addworker.extraVolume.holder': 'e.g. /data/models (path must start with /)'
|
// 67. 'clusters.addworker.extraVolume.holder': 'e.g. /data/models (path must start with /)'
|
||||||
// 68. 'clusters.addworker.vendorNotes.title': 'Notes for {vendor} Device',
|
// 68. 'clusters.addworker.vendorNotes.title': 'Notes for {vendor} Device',
|
||||||
// 69. 'clusters.button.genToken': 'Need to create a new token? Click <a href="{link}" target="_blank">here</a>.',
|
// 69. 'clusters.button.genToken': 'Need to create a new token? Click <a href="{link}" target="_blank">here</a>.',
|
||||||
// 70. '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>.`
|
// 70. '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>.`,
|
||||||
|
// 71. 'clusters.addworker.cacheVolume': 'Model Cache Volume Mount',
|
||||||
|
// 72. 'clusters.addworker.cacheVolume.tips': 'If you want to custom 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 /)',
|
||||||
// ========== End of To-Do List ==========
|
// ========== End of To-Do List ==========
|
||||||
|
|||||||
@@ -73,6 +73,9 @@ export default {
|
|||||||
'clusters.addworker.enterWorkerIP': 'Введите IP воркера',
|
'clusters.addworker.enterWorkerIP': 'Введите IP воркера',
|
||||||
'clusters.addworker.enterWorkerIP.error': 'Пожалуйста, введите IP воркера.',
|
'clusters.addworker.enterWorkerIP.error': 'Пожалуйста, введите IP воркера.',
|
||||||
'clusters.addworker.extraVolume': 'Дополнительное монтирование тома',
|
'clusters.addworker.extraVolume': 'Дополнительное монтирование тома',
|
||||||
|
'clusters.addworker.cacheVolume': 'Model Cache Volume Mount',
|
||||||
|
'clusters.addworker.cacheVolume.tips':
|
||||||
|
'If you want to custom the model cache directory, you can specify the path to mount it.',
|
||||||
'clusters.addworker.configSummary': 'Сводка конфигурации',
|
'clusters.addworker.configSummary': 'Сводка конфигурации',
|
||||||
'clusters.addworker.gpuVendor': 'Производитель GPU',
|
'clusters.addworker.gpuVendor': 'Производитель GPU',
|
||||||
'clusters.addworker.workerIP': 'IP воркера',
|
'clusters.addworker.workerIP': 'IP воркера',
|
||||||
@@ -80,6 +83,8 @@ export default {
|
|||||||
'clusters.addworker.autoDetect': 'Автоопределение',
|
'clusters.addworker.autoDetect': 'Автоопределение',
|
||||||
'clusters.addworker.extraVolume.holder':
|
'clusters.addworker.extraVolume.holder':
|
||||||
'напр. /data/models (путь должен начинаться с /)',
|
'напр. /data/models (путь должен начинаться с /)',
|
||||||
|
'clusters.addworker.cacheVolume.holder':
|
||||||
|
'напр. /data/cache (путь должен начинаться с /)',
|
||||||
'clusters.addworker.vendorNotes.title': 'Примечания для устройств {vendor}',
|
'clusters.addworker.vendorNotes.title': 'Примечания для устройств {vendor}',
|
||||||
'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>.',
|
||||||
@@ -89,4 +94,6 @@ export default {
|
|||||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
// 1. '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>.`,
|
// 1. '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>.`,
|
||||||
// 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',
|
||||||
|
// 4. 'clusters.addworker.cacheVolume.tips': 'If you want to custom the model cache directory, you can specify the path to mount it.',
|
||||||
// ================================================================
|
// ================================================================
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export default {
|
|||||||
'clusters.addworker.nvidiaNotes-01':
|
'clusters.addworker.nvidiaNotes-01':
|
||||||
'如果节点有多个出站 IP 地址,请填写 <span class="bold-text">WORKER_IP</span>,以确保使用指定的 IP。可通过命令 <span class="bold-text">hostname -I | xargs -n1</span> 进行确认。',
|
'如果节点有多个出站 IP 地址,请填写 <span class="bold-text">WORKER_IP</span>,以确保使用指定的 IP。可通过命令 <span class="bold-text">hostname -I | xargs -n1</span> 进行确认。',
|
||||||
'clusters.addworker.nvidiaNotes-02':
|
'clusters.addworker.nvidiaNotes-02':
|
||||||
'如果模型目录已存在于节点上,请添加 <span class="bold-text">--volume</span> 参数进行挂载。',
|
'如果 worker 上已经存在模型目录,你可以指定该路径进行挂载。',
|
||||||
'clusters.addworker.hygonNotes':
|
'clusters.addworker.hygonNotes':
|
||||||
'如果 <span class="bold-text">/opt/hyhal</span> 目录不存在,请创建指向 Hygon 安装路径的符号链接:<span class="bold-text">/opt/hyhal</span>。与 <span class="bold-text">/opt/dtk</span> 目录相同。',
|
'如果 <span class="bold-text">/opt/hyhal</span> 目录不存在,请创建指向 Hygon 安装路径的符号链接:<span class="bold-text">/opt/hyhal</span>。与 <span class="bold-text">/opt/dtk</span> 目录相同。',
|
||||||
'clusters.addworker.corexNotes':
|
'clusters.addworker.corexNotes':
|
||||||
@@ -71,6 +71,9 @@ export default {
|
|||||||
'clusters.addworker.enterWorkerIP': '输入节点 IP',
|
'clusters.addworker.enterWorkerIP': '输入节点 IP',
|
||||||
'clusters.addworker.enterWorkerIP.error': '请输入节点 IP',
|
'clusters.addworker.enterWorkerIP.error': '请输入节点 IP',
|
||||||
'clusters.addworker.extraVolume': '额外卷挂载',
|
'clusters.addworker.extraVolume': '额外卷挂载',
|
||||||
|
'clusters.addworker.cacheVolume': '缓存卷挂载',
|
||||||
|
'clusters.addworker.cacheVolume.tips':
|
||||||
|
'如果你想自定义模型缓存目录,可以指定路径进行挂载。',
|
||||||
'clusters.addworker.configSummary': '配置摘要',
|
'clusters.addworker.configSummary': '配置摘要',
|
||||||
'clusters.addworker.gpuVendor': 'GPU 厂商',
|
'clusters.addworker.gpuVendor': 'GPU 厂商',
|
||||||
'clusters.addworker.workerIP': '节点 IP',
|
'clusters.addworker.workerIP': '节点 IP',
|
||||||
@@ -78,6 +81,8 @@ export default {
|
|||||||
'clusters.addworker.autoDetect': '自动检测',
|
'clusters.addworker.autoDetect': '自动检测',
|
||||||
'clusters.addworker.extraVolume.holder':
|
'clusters.addworker.extraVolume.holder':
|
||||||
'例如:/data/models(路径需以 / 开头)',
|
'例如:/data/models(路径需以 / 开头)',
|
||||||
|
'clusters.addworker.cacheVolume.holder':
|
||||||
|
'例如:/data/cache(路径需以 / 开头)',
|
||||||
'clusters.addworker.vendorNotes.title': '{vendor}设备注意事项',
|
'clusters.addworker.vendorNotes.title': '{vendor}设备注意事项',
|
||||||
'clusters.button.genToken':
|
'clusters.button.genToken':
|
||||||
'需要创建令牌?点击<a href="{link}" target="_blank">这里</a>。',
|
'需要创建令牌?点击<a href="{link}" target="_blank">这里</a>。',
|
||||||
|
|||||||
@@ -316,8 +316,12 @@ const VersionsForm: React.FC<AddModalProps> = ({
|
|||||||
>
|
>
|
||||||
<SealInput.Input
|
<SealInput.Input
|
||||||
trim
|
trim
|
||||||
label={intl.formatMessage({ id: 'backend.imageName' })}
|
|
||||||
required
|
required
|
||||||
|
placeholder={intl.formatMessage(
|
||||||
|
{ id: 'common.help.eg' },
|
||||||
|
{ content: 'vllm/vllm-openai:v0.12.0' }
|
||||||
|
)}
|
||||||
|
label={intl.formatMessage({ id: 'backend.imageName' })}
|
||||||
></SealInput.Input>
|
></SealInput.Input>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ type ViewModalProps = {
|
|||||||
currentGPU?: string;
|
currentGPU?: string;
|
||||||
workerIP?: string;
|
workerIP?: string;
|
||||||
modelDir?: string;
|
modelDir?: string;
|
||||||
|
cacheDir?: string;
|
||||||
registrationInfo: {
|
registrationInfo: {
|
||||||
token: string;
|
token: string;
|
||||||
image: string;
|
image: string;
|
||||||
@@ -17,6 +18,7 @@ const AddWorkerCommand: React.FC<ViewModalProps> = ({
|
|||||||
registrationInfo,
|
registrationInfo,
|
||||||
workerIP,
|
workerIP,
|
||||||
modelDir,
|
modelDir,
|
||||||
|
cacheDir,
|
||||||
currentGPU
|
currentGPU
|
||||||
}) => {
|
}) => {
|
||||||
const code = React.useMemo(() => {
|
const code = React.useMemo(() => {
|
||||||
@@ -28,13 +30,14 @@ const AddWorkerCommand: React.FC<ViewModalProps> = ({
|
|||||||
tag: '',
|
tag: '',
|
||||||
workerIP: workerIP,
|
workerIP: workerIP,
|
||||||
modelDir: modelDir,
|
modelDir: modelDir,
|
||||||
|
cacheDir: cacheDir,
|
||||||
image: registrationInfo.image,
|
image: registrationInfo.image,
|
||||||
token: registrationInfo.token || '${token}'
|
token: registrationInfo.token || '${token}'
|
||||||
})
|
})
|
||||||
?.trim()
|
?.trim()
|
||||||
.replace(/\s+$/gm, '')
|
.replace(/\s+$/gm, '')
|
||||||
.replace(/\\+$/, '');
|
.replace(/\\+$/, '');
|
||||||
}, [registrationInfo, currentGPU, workerIP, modelDir]);
|
}, [registrationInfo, currentGPU, workerIP, modelDir, cacheDir]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HighlightCode
|
<HighlightCode
|
||||||
|
|||||||
@@ -42,6 +42,10 @@ export interface SummaryDataKeys {
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
path: string;
|
path: string;
|
||||||
};
|
};
|
||||||
|
cacheDirConfig: {
|
||||||
|
enabled: boolean;
|
||||||
|
path: string;
|
||||||
|
};
|
||||||
workerIPConfig: {
|
workerIPConfig: {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
ip: string;
|
ip: string;
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ const DockerRunCommand = () => {
|
|||||||
path: '',
|
path: '',
|
||||||
required: false
|
required: false
|
||||||
};
|
};
|
||||||
|
const cacheDirConfig = summary.get('cacheDirConfig') || {
|
||||||
|
enable: false,
|
||||||
|
path: ''
|
||||||
|
};
|
||||||
const currentGPU = summary.get('currentGPU') || '';
|
const currentGPU = summary.get('currentGPU') || '';
|
||||||
|
|
||||||
const stepIndex = stepList.indexOf(StepNamesMap.RunCommand) + 1;
|
const stepIndex = stepList.indexOf(StepNamesMap.RunCommand) + 1;
|
||||||
@@ -51,6 +55,7 @@ const DockerRunCommand = () => {
|
|||||||
registrationInfo={registrationInfo}
|
registrationInfo={registrationInfo}
|
||||||
workerIP={workerIPConfig.enable ? workerIPConfig.ip : ''}
|
workerIP={workerIPConfig.enable ? workerIPConfig.ip : ''}
|
||||||
modelDir={modelDirConfig.enable ? modelDirConfig.path : ''}
|
modelDir={modelDirConfig.enable ? modelDirConfig.path : ''}
|
||||||
|
cacheDir={cacheDirConfig.enable ? cacheDirConfig.path : ''}
|
||||||
currentGPU={currentGPU}
|
currentGPU={currentGPU}
|
||||||
/>
|
/>
|
||||||
</StepCollapse>
|
</StepCollapse>
|
||||||
|
|||||||
@@ -2,12 +2,79 @@ import AlertInfoBlock from '@/components/alert-info/block';
|
|||||||
import { ExclamationCircleFilled } from '@ant-design/icons';
|
import { ExclamationCircleFilled } from '@ant-design/icons';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Input, Switch } from 'antd';
|
import { Input, Switch } from 'antd';
|
||||||
import { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
import { useAddWorkerContext } from './add-worker-context';
|
import { useAddWorkerContext } from './add-worker-context';
|
||||||
import { StepNamesMap } from './config';
|
import { StepNamesMap } from './config';
|
||||||
import { NotesWrapper, SwitchWrapper, Tips, Title } from './constainers';
|
import { NotesWrapper, SwitchWrapper, Tips, Title } from './constainers';
|
||||||
import StepCollapse from './step-collapse';
|
import StepCollapse from './step-collapse';
|
||||||
|
|
||||||
|
const ButtonWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const SwitchSetting: React.FC<{
|
||||||
|
label: string;
|
||||||
|
checked: boolean;
|
||||||
|
value?: string;
|
||||||
|
placeholder?: string;
|
||||||
|
tips?: React.ReactNode;
|
||||||
|
errorMessage?: React.ReactNode;
|
||||||
|
extra?: React.ReactNode;
|
||||||
|
onInputChange?: (value: string) => void;
|
||||||
|
onChange: (checked: boolean) => void;
|
||||||
|
}> = ({
|
||||||
|
label,
|
||||||
|
checked,
|
||||||
|
onChange,
|
||||||
|
value,
|
||||||
|
placeholder,
|
||||||
|
tips,
|
||||||
|
extra,
|
||||||
|
errorMessage,
|
||||||
|
onInputChange
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<SwitchWrapper>
|
||||||
|
<ButtonWrapper>
|
||||||
|
<span style={{ color: 'var(--ant-color-text)', fontWeight: 500 }}>
|
||||||
|
<span>{label}</span>
|
||||||
|
</span>
|
||||||
|
<Switch checked={checked} onChange={onChange}></Switch>
|
||||||
|
</ButtonWrapper>
|
||||||
|
{tips && (
|
||||||
|
<Tips
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: tips
|
||||||
|
}}
|
||||||
|
></Tips>
|
||||||
|
)}
|
||||||
|
{checked && (
|
||||||
|
<>
|
||||||
|
<Input
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
value={value}
|
||||||
|
placeholder={placeholder}
|
||||||
|
onChange={(e) => onInputChange?.(e.target.value)}
|
||||||
|
/>
|
||||||
|
{errorMessage && (
|
||||||
|
<Tips
|
||||||
|
style={{
|
||||||
|
color: 'var(--ant-color-error)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{errorMessage}
|
||||||
|
</Tips>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{extra}
|
||||||
|
</SwitchWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const SpecifyArguments = () => {
|
const SpecifyArguments = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { stepList, summary, updateField, registerField } =
|
const { stepList, summary, updateField, registerField } =
|
||||||
@@ -24,6 +91,11 @@ const SpecifyArguments = () => {
|
|||||||
path: ''
|
path: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const cacheDirConfig = summary.get('cacheDirConfig') || {
|
||||||
|
enable: false,
|
||||||
|
path: ''
|
||||||
|
};
|
||||||
|
|
||||||
const setWorkerIPConfig = (config: {
|
const setWorkerIPConfig = (config: {
|
||||||
enable: boolean;
|
enable: boolean;
|
||||||
ip?: string;
|
ip?: string;
|
||||||
@@ -42,6 +114,13 @@ const SpecifyArguments = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const setCacheDirConfig = (config: { enable: boolean; path?: string }) => {
|
||||||
|
updateField('cacheDirConfig', {
|
||||||
|
...cacheDirConfig,
|
||||||
|
...config
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const beforeNext = async () => {
|
const beforeNext = async () => {
|
||||||
if (workerIPConfig.enable && !workerIPConfig.ip) {
|
if (workerIPConfig.enable && !workerIPConfig.ip) {
|
||||||
setWorkerIPConfig({
|
setWorkerIPConfig({
|
||||||
@@ -56,16 +135,13 @@ const SpecifyArguments = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unregister = registerField('workerIPConfig');
|
const unregisterWorkerIP = registerField('workerIPConfig');
|
||||||
|
const unregisterModelDir = registerField('modelDirConfig');
|
||||||
|
const unregisterCacheDir = registerField('cacheDirConfig');
|
||||||
return () => {
|
return () => {
|
||||||
unregister();
|
unregisterWorkerIP();
|
||||||
};
|
unregisterModelDir();
|
||||||
}, []);
|
unregisterCacheDir();
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const unregister = registerField('modelDirConfig');
|
|
||||||
return () => {
|
|
||||||
unregister();
|
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -80,6 +156,11 @@ const SpecifyArguments = () => {
|
|||||||
enable: false,
|
enable: false,
|
||||||
path: ''
|
path: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
|
updateField('cacheDirConfig', {
|
||||||
|
enable: false,
|
||||||
|
path: ''
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -102,123 +183,111 @@ const SpecifyArguments = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* worker IP config */}
|
{/* worker IP config */}
|
||||||
<SwitchWrapper>
|
<SwitchSetting
|
||||||
<div className="button">
|
label={
|
||||||
<span style={{ color: 'var(--ant-color-text)', fontWeight: 500 }}>
|
workerIPConfig.enable
|
||||||
{workerIPConfig.enable
|
? intl.formatMessage({
|
||||||
? intl.formatMessage({
|
id: 'clusters.addworker.specifyWorkerIP'
|
||||||
id: 'clusters.addworker.specifyWorkerIP'
|
|
||||||
})
|
|
||||||
: intl.formatMessage({
|
|
||||||
id: 'clusters.addworker.detectWorkerIP'
|
|
||||||
})}
|
|
||||||
</span>
|
|
||||||
<Switch
|
|
||||||
checked={workerIPConfig.enable}
|
|
||||||
onChange={(checked) =>
|
|
||||||
setWorkerIPConfig({
|
|
||||||
...workerIPConfig,
|
|
||||||
enable: checked,
|
|
||||||
required: false
|
|
||||||
})
|
})
|
||||||
}
|
: intl.formatMessage({
|
||||||
></Switch>
|
id: 'clusters.addworker.detectWorkerIP'
|
||||||
</div>
|
})
|
||||||
{workerIPConfig.enable && (
|
}
|
||||||
<>
|
placeholder={intl.formatMessage({
|
||||||
<Input
|
id: 'clusters.addworker.enterWorkerIP'
|
||||||
style={{ width: '100%' }}
|
})}
|
||||||
placeholder={intl.formatMessage({
|
value={workerIPConfig.ip}
|
||||||
id: 'clusters.addworker.enterWorkerIP'
|
checked={workerIPConfig.enable}
|
||||||
})}
|
errorMessage={
|
||||||
value={workerIPConfig.ip}
|
workerIPConfig.required &&
|
||||||
onChange={(e) =>
|
!workerIPConfig.ip &&
|
||||||
setWorkerIPConfig({
|
intl.formatMessage({
|
||||||
...workerIPConfig,
|
id: 'clusters.addworker.enterWorkerIP.error'
|
||||||
ip: e.target.value
|
})
|
||||||
})
|
}
|
||||||
|
onChange={(checked) =>
|
||||||
|
setWorkerIPConfig({
|
||||||
|
...workerIPConfig,
|
||||||
|
enable: checked,
|
||||||
|
required: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onInputChange={(value) =>
|
||||||
|
setWorkerIPConfig({
|
||||||
|
...workerIPConfig,
|
||||||
|
ip: value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
extra={
|
||||||
|
!workerIPConfig.enable && (
|
||||||
|
<AlertInfoBlock
|
||||||
|
maxHeight={200}
|
||||||
|
contentStyle={{
|
||||||
|
paddingLeft: 0
|
||||||
|
}}
|
||||||
|
type="warning"
|
||||||
|
icon={<ExclamationCircleFilled />}
|
||||||
|
message={
|
||||||
|
<NotesWrapper>
|
||||||
|
<li
|
||||||
|
style={{
|
||||||
|
marginLeft: '0 !important',
|
||||||
|
listStyleType: 'none'
|
||||||
|
}}
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: intl.formatMessage({
|
||||||
|
id: 'clusters.addworker.nvidiaNotes-01'
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
></li>
|
||||||
|
</NotesWrapper>
|
||||||
}
|
}
|
||||||
/>
|
></AlertInfoBlock>
|
||||||
{workerIPConfig.required && !workerIPConfig.ip && (
|
)
|
||||||
<Tips
|
}
|
||||||
style={{
|
></SwitchSetting>
|
||||||
color: 'var(--ant-color-error)'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{intl.formatMessage({
|
|
||||||
id: 'clusters.addworker.enterWorkerIP.error'
|
|
||||||
})}
|
|
||||||
</Tips>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{!workerIPConfig.enable && (
|
|
||||||
<AlertInfoBlock
|
|
||||||
maxHeight={200}
|
|
||||||
contentStyle={{
|
|
||||||
paddingLeft: 0
|
|
||||||
}}
|
|
||||||
style={{ marginBottom: 8 }}
|
|
||||||
type="warning"
|
|
||||||
icon={<ExclamationCircleFilled />}
|
|
||||||
message={
|
|
||||||
<NotesWrapper>
|
|
||||||
<li
|
|
||||||
style={{
|
|
||||||
marginLeft: '0 !important',
|
|
||||||
listStyleType: 'none'
|
|
||||||
}}
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: intl.formatMessage({
|
|
||||||
id: 'clusters.addworker.nvidiaNotes-01'
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
></li>
|
|
||||||
</NotesWrapper>
|
|
||||||
}
|
|
||||||
></AlertInfoBlock>
|
|
||||||
)}
|
|
||||||
</SwitchWrapper>
|
|
||||||
{/* model directory config */}
|
{/* model directory config */}
|
||||||
<SwitchWrapper>
|
<SwitchSetting
|
||||||
<div className="button">
|
label={intl.formatMessage({ id: 'clusters.addworker.extraVolume' })}
|
||||||
<span style={{ color: 'var(--ant-color-text)', fontWeight: 500 }}>
|
tips={intl.formatMessage({
|
||||||
{/* optional */}
|
id: 'clusters.addworker.nvidiaNotes-02'
|
||||||
<span>
|
})}
|
||||||
{intl.formatMessage({ id: 'clusters.addworker.extraVolume' })}
|
placeholder={intl.formatMessage({
|
||||||
</span>
|
id: 'clusters.addworker.extraVolume.holder'
|
||||||
</span>
|
})}
|
||||||
|
checked={modelDirConfig.enable}
|
||||||
<Switch
|
value={modelDirConfig.path}
|
||||||
checked={modelDirConfig.enable}
|
onChange={(checked) =>
|
||||||
onChange={(checked) =>
|
setModelDirConfig({ ...modelDirConfig, enable: checked })
|
||||||
setModelDirConfig({ ...modelDirConfig, enable: checked })
|
}
|
||||||
}
|
onInputChange={(value) =>
|
||||||
></Switch>
|
setModelDirConfig({
|
||||||
</div>
|
...modelDirConfig,
|
||||||
<Tips
|
path: value
|
||||||
dangerouslySetInnerHTML={{
|
})
|
||||||
__html: intl.formatMessage({
|
}
|
||||||
id: 'clusters.addworker.nvidiaNotes-02'
|
></SwitchSetting>
|
||||||
})
|
{/* cache directory config */}
|
||||||
}}
|
<SwitchSetting
|
||||||
></Tips>
|
label={intl.formatMessage({ id: 'clusters.addworker.cacheVolume' })}
|
||||||
{modelDirConfig.enable && (
|
tips={intl.formatMessage({
|
||||||
<Input
|
id: 'clusters.addworker.cacheVolume.tips'
|
||||||
style={{ width: '100%' }}
|
})}
|
||||||
value={modelDirConfig.path}
|
placeholder={intl.formatMessage({
|
||||||
placeholder={intl.formatMessage({
|
id: 'clusters.addworker.cacheVolume.holder'
|
||||||
id: 'clusters.addworker.extraVolume.holder'
|
})}
|
||||||
})}
|
checked={cacheDirConfig.enable}
|
||||||
onChange={(e) =>
|
value={cacheDirConfig.path}
|
||||||
setModelDirConfig({
|
onChange={(checked) =>
|
||||||
...modelDirConfig,
|
setCacheDirConfig({ ...cacheDirConfig, enable: checked })
|
||||||
path: e.target.value
|
}
|
||||||
})
|
onInputChange={(value) =>
|
||||||
}
|
setCacheDirConfig({
|
||||||
/>
|
...cacheDirConfig,
|
||||||
)}
|
path: value
|
||||||
</SwitchWrapper>
|
})
|
||||||
|
}
|
||||||
|
></SwitchSetting>
|
||||||
</div>
|
</div>
|
||||||
</StepCollapse>
|
</StepCollapse>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -27,6 +27,11 @@ const SummaryData: React.FC = () => {
|
|||||||
path: ''
|
path: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const cacheDirConfig = summary.get('cacheDirConfig') || {
|
||||||
|
enable: false,
|
||||||
|
path: ''
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConfigWrapper>
|
<ConfigWrapper>
|
||||||
<Title>
|
<Title>
|
||||||
@@ -129,6 +134,32 @@ const SummaryData: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="item">
|
||||||
|
<span className="label">
|
||||||
|
{intl.formatMessage({ id: 'clusters.addworker.cacheVolume' })}:
|
||||||
|
</span>
|
||||||
|
<span className="value">
|
||||||
|
{cacheDirConfig.enable && cacheDirConfig.path
|
||||||
|
? cacheDirConfig.path
|
||||||
|
: ''}
|
||||||
|
{(!cacheDirConfig.path || !cacheDirConfig.enable) && (
|
||||||
|
<StopOutlined
|
||||||
|
style={{
|
||||||
|
color: 'var(--ant-color-text-tertiary)'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{cacheDirConfig.enable && cacheDirConfig.path && (
|
||||||
|
<CheckCircleOutlined
|
||||||
|
style={{
|
||||||
|
color: 'var(--ant-color-success)',
|
||||||
|
marginLeft: 4
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ConfigWrapper>
|
</ConfigWrapper>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ const AccessControlModal: React.FC<
|
|||||||
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
||||||
onCancel?.();
|
onCancel?.();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error(intl.formatMessage({ id: 'common.message.failed' }));
|
// do nothing, error message handled in request layer
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import CollapsePanel from '@/pages/_components/collapse-panel';
|
|||||||
import { useWrapperContext } from '@/pages/_components/column-wrapper/use-wrapper-context';
|
import { useWrapperContext } from '@/pages/_components/column-wrapper/use-wrapper-context';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import useMemoizedFn from 'ahooks/lib/useMemoizedFn';
|
import useMemoizedFn from 'ahooks/lib/useMemoizedFn';
|
||||||
import { Form, Segmented } from 'antd';
|
import { Form } from 'antd';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { forwardRef, useImperativeHandle, useMemo } from 'react';
|
import React, { forwardRef, useImperativeHandle, useMemo } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
@@ -42,14 +42,6 @@ const scheduleRequiredFields = ['gpu_selector'];
|
|||||||
|
|
||||||
const performanceRequiredFields = ['speculative_config'];
|
const performanceRequiredFields = ['speculative_config'];
|
||||||
|
|
||||||
const SegmentedInner = styled(Segmented)`
|
|
||||||
width: 100%;
|
|
||||||
border-radius: 0;
|
|
||||||
.ant-segmented-item {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const SegmentedHeader = styled.div<{ $top?: number }>`
|
const SegmentedHeader = styled.div<{ $top?: number }>`
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: ${(props) => props.$top || 0}px;
|
top: ${(props) => props.$top || 0}px;
|
||||||
@@ -260,9 +252,10 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
|||||||
await onClusterChange?.(value);
|
await onClusterChange?.(value);
|
||||||
getGPUOptionList({ clusterId: value });
|
getGPUOptionList({ clusterId: value });
|
||||||
getBackendOptions({ cluster_id: value });
|
getBackendOptions({ cluster_id: value });
|
||||||
if (scheduleType === ScheduleValueMap.Manual) {
|
form.setFieldsValue({
|
||||||
form.setFieldValue(['gpu_selector', 'gpu_ids'], []);
|
scheduleType: ScheduleValueMap.Auto,
|
||||||
}
|
gpu_selector: null
|
||||||
|
});
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
setTimeout(resolve, 150);
|
setTimeout(resolve, 150);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -174,6 +174,7 @@ export const useCheckCompatibility = () => {
|
|||||||
const handleEvaluate = async (data: any) => {
|
const handleEvaluate = async (data: any) => {
|
||||||
try {
|
try {
|
||||||
// when no cluster selected, show warning and prompt user to add cluster first
|
// when no cluster selected, show warning and prompt user to add cluster first
|
||||||
|
console.log('handleEvaluate', data);
|
||||||
if (!data.cluster_id) {
|
if (!data.cluster_id) {
|
||||||
setWarningStatus({
|
setWarningStatus({
|
||||||
show: true,
|
show: true,
|
||||||
@@ -388,7 +389,7 @@ export const useCheckCompatibility = () => {
|
|||||||
const handleDoEvalute = async (formData: FormData) => {
|
const handleDoEvalute = async (formData: FormData) => {
|
||||||
const currentRequestId = updateRequestId();
|
const currentRequestId = updateRequestId();
|
||||||
const evalutionData = await handleEvaluate(formData);
|
const evalutionData = await handleEvaluate(formData);
|
||||||
if (currentRequestId === requestIdRef.current) {
|
if (currentRequestId === requestIdRef.current && evalutionData) {
|
||||||
handleShowCompatibleAlert?.(evalutionData);
|
handleShowCompatibleAlert?.(evalutionData);
|
||||||
return evalutionData;
|
return evalutionData;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,13 +127,15 @@ export const dockerEnvCommandMap = {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
const setNormalArgs = (params: any) => {
|
const setNormalArgs = (params: any) => {
|
||||||
|
console.log('params++++++++++++++', params);
|
||||||
return `sudo docker run -d --name gpustack-worker \\
|
return `sudo docker run -d --name gpustack-worker \\
|
||||||
--restart=unless-stopped \\
|
--restart=unless-stopped \\
|
||||||
--privileged \\
|
--privileged \\
|
||||||
--network=host \\
|
--network=host \\
|
||||||
--volume /var/run/docker.sock:/var/run/docker.sock \\
|
--volume /var/run/docker.sock:/var/run/docker.sock \\
|
||||||
--volume gpustack-data:/var/lib/gpustack \\
|
--volume gpustack-data:/var/lib/gpustack \\
|
||||||
${params.modelDir ? `--volume ${params.modelDir}:${params.modelDir} \\` : ''}`;
|
${params.modelDir ? `--volume ${params.modelDir}:${params.modelDir} \\` : ''}
|
||||||
|
${params.cacheDir ? `--volume ${params.cacheDir}:/var/lib/gpustack/cache \\` : ''}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const setImageArgs = (params: any) => {
|
const setImageArgs = (params: any) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user