feat: model cache volume mount, clear gpu selector

This commit is contained in:
jialin
2025-12-09 19:07:40 +08:00
parent daa10617c0
commit 087675889f
14 changed files with 280 additions and 143 deletions
@@ -6,6 +6,7 @@ type ViewModalProps = {
currentGPU?: string;
workerIP?: string;
modelDir?: string;
cacheDir?: string;
registrationInfo: {
token: string;
image: string;
@@ -17,6 +18,7 @@ const AddWorkerCommand: React.FC<ViewModalProps> = ({
registrationInfo,
workerIP,
modelDir,
cacheDir,
currentGPU
}) => {
const code = React.useMemo(() => {
@@ -28,13 +30,14 @@ const AddWorkerCommand: React.FC<ViewModalProps> = ({
tag: '',
workerIP: workerIP,
modelDir: modelDir,
cacheDir: cacheDir,
image: registrationInfo.image,
token: registrationInfo.token || '${token}'
})
?.trim()
.replace(/\s+$/gm, '')
.replace(/\\+$/, '');
}, [registrationInfo, currentGPU, workerIP, modelDir]);
}, [registrationInfo, currentGPU, workerIP, modelDir, cacheDir]);
return (
<HighlightCode
@@ -42,6 +42,10 @@ export interface SummaryDataKeys {
enabled: boolean;
path: string;
};
cacheDirConfig: {
enabled: boolean;
path: string;
};
workerIPConfig: {
enabled: boolean;
ip: string;
@@ -21,6 +21,10 @@ const DockerRunCommand = () => {
path: '',
required: false
};
const cacheDirConfig = summary.get('cacheDirConfig') || {
enable: false,
path: ''
};
const currentGPU = summary.get('currentGPU') || '';
const stepIndex = stepList.indexOf(StepNamesMap.RunCommand) + 1;
@@ -51,6 +55,7 @@ const DockerRunCommand = () => {
registrationInfo={registrationInfo}
workerIP={workerIPConfig.enable ? workerIPConfig.ip : ''}
modelDir={modelDirConfig.enable ? modelDirConfig.path : ''}
cacheDir={cacheDirConfig.enable ? cacheDirConfig.path : ''}
currentGPU={currentGPU}
/>
</StepCollapse>
@@ -2,12 +2,79 @@ import AlertInfoBlock from '@/components/alert-info/block';
import { ExclamationCircleFilled } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
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 { StepNamesMap } from './config';
import { NotesWrapper, SwitchWrapper, Tips, Title } from './constainers';
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 intl = useIntl();
const { stepList, summary, updateField, registerField } =
@@ -24,6 +91,11 @@ const SpecifyArguments = () => {
path: ''
};
const cacheDirConfig = summary.get('cacheDirConfig') || {
enable: false,
path: ''
};
const setWorkerIPConfig = (config: {
enable: boolean;
ip?: string;
@@ -42,6 +114,13 @@ const SpecifyArguments = () => {
});
};
const setCacheDirConfig = (config: { enable: boolean; path?: string }) => {
updateField('cacheDirConfig', {
...cacheDirConfig,
...config
});
};
const beforeNext = async () => {
if (workerIPConfig.enable && !workerIPConfig.ip) {
setWorkerIPConfig({
@@ -56,16 +135,13 @@ const SpecifyArguments = () => {
};
useEffect(() => {
const unregister = registerField('workerIPConfig');
const unregisterWorkerIP = registerField('workerIPConfig');
const unregisterModelDir = registerField('modelDirConfig');
const unregisterCacheDir = registerField('cacheDirConfig');
return () => {
unregister();
};
}, []);
useEffect(() => {
const unregister = registerField('modelDirConfig');
return () => {
unregister();
unregisterWorkerIP();
unregisterModelDir();
unregisterCacheDir();
};
}, []);
@@ -80,6 +156,11 @@ const SpecifyArguments = () => {
enable: false,
path: ''
});
updateField('cacheDirConfig', {
enable: false,
path: ''
});
}, []);
return (
@@ -102,123 +183,111 @@ const SpecifyArguments = () => {
}}
>
{/* worker IP config */}
<SwitchWrapper>
<div className="button">
<span style={{ color: 'var(--ant-color-text)', fontWeight: 500 }}>
{workerIPConfig.enable
? intl.formatMessage({
id: 'clusters.addworker.specifyWorkerIP'
})
: intl.formatMessage({
id: 'clusters.addworker.detectWorkerIP'
})}
</span>
<Switch
checked={workerIPConfig.enable}
onChange={(checked) =>
setWorkerIPConfig({
...workerIPConfig,
enable: checked,
required: false
<SwitchSetting
label={
workerIPConfig.enable
? intl.formatMessage({
id: 'clusters.addworker.specifyWorkerIP'
})
}
></Switch>
</div>
{workerIPConfig.enable && (
<>
<Input
style={{ width: '100%' }}
placeholder={intl.formatMessage({
id: 'clusters.addworker.enterWorkerIP'
})}
value={workerIPConfig.ip}
onChange={(e) =>
setWorkerIPConfig({
...workerIPConfig,
ip: e.target.value
})
: intl.formatMessage({
id: 'clusters.addworker.detectWorkerIP'
})
}
placeholder={intl.formatMessage({
id: 'clusters.addworker.enterWorkerIP'
})}
value={workerIPConfig.ip}
checked={workerIPConfig.enable}
errorMessage={
workerIPConfig.required &&
!workerIPConfig.ip &&
intl.formatMessage({
id: 'clusters.addworker.enterWorkerIP.error'
})
}
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>
}
/>
{workerIPConfig.required && !workerIPConfig.ip && (
<Tips
style={{
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>
></AlertInfoBlock>
)
}
></SwitchSetting>
{/* model directory config */}
<SwitchWrapper>
<div className="button">
<span style={{ color: 'var(--ant-color-text)', fontWeight: 500 }}>
{/* optional */}
<span>
{intl.formatMessage({ id: 'clusters.addworker.extraVolume' })}
</span>
</span>
<Switch
checked={modelDirConfig.enable}
onChange={(checked) =>
setModelDirConfig({ ...modelDirConfig, enable: checked })
}
></Switch>
</div>
<Tips
dangerouslySetInnerHTML={{
__html: intl.formatMessage({
id: 'clusters.addworker.nvidiaNotes-02'
})
}}
></Tips>
{modelDirConfig.enable && (
<Input
style={{ width: '100%' }}
value={modelDirConfig.path}
placeholder={intl.formatMessage({
id: 'clusters.addworker.extraVolume.holder'
})}
onChange={(e) =>
setModelDirConfig({
...modelDirConfig,
path: e.target.value
})
}
/>
)}
</SwitchWrapper>
<SwitchSetting
label={intl.formatMessage({ id: 'clusters.addworker.extraVolume' })}
tips={intl.formatMessage({
id: 'clusters.addworker.nvidiaNotes-02'
})}
placeholder={intl.formatMessage({
id: 'clusters.addworker.extraVolume.holder'
})}
checked={modelDirConfig.enable}
value={modelDirConfig.path}
onChange={(checked) =>
setModelDirConfig({ ...modelDirConfig, enable: checked })
}
onInputChange={(value) =>
setModelDirConfig({
...modelDirConfig,
path: value
})
}
></SwitchSetting>
{/* cache directory config */}
<SwitchSetting
label={intl.formatMessage({ id: 'clusters.addworker.cacheVolume' })}
tips={intl.formatMessage({
id: 'clusters.addworker.cacheVolume.tips'
})}
placeholder={intl.formatMessage({
id: 'clusters.addworker.cacheVolume.holder'
})}
checked={cacheDirConfig.enable}
value={cacheDirConfig.path}
onChange={(checked) =>
setCacheDirConfig({ ...cacheDirConfig, enable: checked })
}
onInputChange={(value) =>
setCacheDirConfig({
...cacheDirConfig,
path: value
})
}
></SwitchSetting>
</div>
</StepCollapse>
);
@@ -27,6 +27,11 @@ const SummaryData: React.FC = () => {
path: ''
};
const cacheDirConfig = summary.get('cacheDirConfig') || {
enable: false,
path: ''
};
return (
<ConfigWrapper>
<Title>
@@ -129,6 +134,32 @@ const SummaryData: React.FC = () => {
)}
</span>
</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>
</ConfigWrapper>
);