feat: add schedule type ux
This commit is contained in:
@@ -10,6 +10,10 @@
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.m-b-15 {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.m-l-10 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
@@ -18,6 +22,10 @@
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.m-l-4 {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.m-l-2 {
|
||||
margin-left: 2px;
|
||||
}
|
||||
@@ -50,6 +58,14 @@
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.m-l-6 {
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.p-l-6 {
|
||||
padding-left: 6px;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ const Inner: React.FC<LabelSelectorProps> = ({
|
||||
>
|
||||
<PlusOutlined className="font-size-14" />{' '}
|
||||
{intl.formatMessage({
|
||||
id: 'common.button.addLabel'
|
||||
id: 'common.button.addSelector'
|
||||
})}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.wrapper {
|
||||
position: relative;
|
||||
padding: 16px;
|
||||
padding: 14px;
|
||||
padding-top: 34px;
|
||||
border: 1px solid var(--ant-color-border);
|
||||
border-radius: var(--border-radius-base);
|
||||
@@ -10,7 +10,7 @@
|
||||
:global {
|
||||
.label {
|
||||
position: absolute;
|
||||
left: 24px;
|
||||
left: 16px;
|
||||
line-height: 1;
|
||||
top: 12px;
|
||||
color: var(--ant-color-text-tertiary);
|
||||
|
||||
@@ -10,6 +10,7 @@ interface NoteInfoProps {
|
||||
}
|
||||
const NoteInfo: React.FC<NoteInfoProps> = (props) => {
|
||||
const { required, description, label } = props || {};
|
||||
if (!label) return null;
|
||||
|
||||
return (
|
||||
<span className="label-text">
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
padding-inline: 12px;
|
||||
padding-inline: 8px 12px;
|
||||
height: 54px;
|
||||
border-width: var(--ant-line-width);
|
||||
border-style: var(--ant-line-type);
|
||||
@@ -13,12 +13,12 @@
|
||||
border-radius: @borderRadius;
|
||||
background-color: var(--color-white-1);
|
||||
|
||||
&.filled {
|
||||
&.borderless {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&.borderless {
|
||||
&.filled {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
@@ -86,7 +86,7 @@
|
||||
@wrapheight: 54px;
|
||||
@inputheight: 32px;
|
||||
@borderRadius: 8px;
|
||||
@input-inner-padding: 12px;
|
||||
@input-inner-padding: 6px;
|
||||
@bgColor: transparent;
|
||||
|
||||
position: relative;
|
||||
@@ -113,7 +113,7 @@
|
||||
|
||||
.label {
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
left: 6px;
|
||||
color: rgba(0, 0, 0, 45%);
|
||||
font-size: var(--font-size-base);
|
||||
line-height: 1;
|
||||
@@ -131,6 +131,10 @@
|
||||
top: 21px;
|
||||
transition: all 0.2s var(--seal-transition-func);
|
||||
}
|
||||
|
||||
&.has-prefix {
|
||||
top: 10px !important;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== select ==================
|
||||
@@ -171,6 +175,16 @@
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
&.no-label {
|
||||
padding-block: 0;
|
||||
|
||||
:global {
|
||||
.ant-select-arrow {
|
||||
top: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:global(
|
||||
.ant-select-outlined.ant-select-disabled:not(.ant-select-customize-input)
|
||||
.ant-select-selector
|
||||
@@ -191,7 +205,7 @@
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
padding-block: 5px;
|
||||
padding-inline: 12px;
|
||||
padding-inline: @input-inner-padding;
|
||||
height: @inputheight !important;
|
||||
background-color: @bgColor;
|
||||
}
|
||||
@@ -278,9 +292,9 @@
|
||||
height: @wrapheight - 2px;
|
||||
}
|
||||
|
||||
:global(.ant-input-prefix) {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
left: -4px;
|
||||
}
|
||||
// :global(.ant-input-prefix) {
|
||||
// position: absolute;
|
||||
// top: 1px;
|
||||
// left: -4px;
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ interface WrapperProps {
|
||||
extra?: React.ReactNode;
|
||||
addAfter?: React.ReactNode;
|
||||
variant?: string;
|
||||
hasPrefix?: boolean;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
@@ -30,6 +31,7 @@ const Wrapper: React.FC<WrapperProps> = ({
|
||||
extra,
|
||||
variant,
|
||||
addAfter,
|
||||
hasPrefix,
|
||||
noWrapperStyle,
|
||||
onClick
|
||||
}) => {
|
||||
@@ -41,12 +43,14 @@ const Wrapper: React.FC<WrapperProps> = ({
|
||||
wrapperStyle[`validate-status-${status}`],
|
||||
addAfter ? wrapperStyle['seal-input-wrapper-addafter'] : '',
|
||||
disabled ? wrapperStyle['seal-input-wrapper-disabled'] : '',
|
||||
className ? wrapperStyle[className] : ''
|
||||
className ? wrapperStyle[className] : '',
|
||||
variant ? wrapperStyle[variant] : ''
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={classNames(wrapperStyle.wrapper, {
|
||||
[wrapperStyle['no-wrapper-style']]: noWrapperStyle
|
||||
[wrapperStyle['no-wrapper-style']]: noWrapperStyle,
|
||||
[wrapperStyle['no-label']]: !label
|
||||
})}
|
||||
onClick={onClick}
|
||||
>
|
||||
@@ -56,7 +60,10 @@ const Wrapper: React.FC<WrapperProps> = ({
|
||||
wrapperStyle['label'],
|
||||
isFocus
|
||||
? wrapperStyle['isfoucs-has-value']
|
||||
: wrapperStyle['blur-no-value']
|
||||
: wrapperStyle['blur-no-value'],
|
||||
{
|
||||
[wrapperStyle['has-prefix']]: hasPrefix
|
||||
}
|
||||
)}
|
||||
>
|
||||
<LabelInfo
|
||||
|
||||
@@ -62,6 +62,7 @@ const SealPassword: React.FC<InputProps & SealFormItemProps> = (props) => {
|
||||
required={required}
|
||||
description={description}
|
||||
disabled={props.disabled}
|
||||
hasPrefix={!!props.prefix}
|
||||
onClick={handleClickWrapper}
|
||||
>
|
||||
<Input.Password
|
||||
|
||||
@@ -77,6 +77,7 @@ const SealInput: React.FC<InputProps & SealFormItemProps> = (props) => {
|
||||
description={description}
|
||||
disabled={props.disabled}
|
||||
addAfter={addAfter}
|
||||
hasPrefix={!!props.prefix}
|
||||
onClick={handleClickWrapper}
|
||||
>
|
||||
<Input
|
||||
|
||||
@@ -203,5 +203,6 @@ export default {
|
||||
'common.button.docs': 'Documentation',
|
||||
'common.button.version': 'Version',
|
||||
'common.title.delete.confirm': 'Confirm delete',
|
||||
'common.button.addLabel': 'Add Label'
|
||||
'common.button.addLabel': 'Add Labels',
|
||||
'common.button.addSelector': 'Add Selectors'
|
||||
};
|
||||
|
||||
@@ -12,9 +12,9 @@ export default {
|
||||
'models.form.configurations': 'Configurations',
|
||||
'models.form.s3address': 'S3 Address',
|
||||
'models.form.partialoffload.tips':
|
||||
'refers to the strategy of offloading as many layers of a model to the GPU as possible based on available resources, while offload all layers completely is not possible.',
|
||||
'Refers to the strategy of offloading as many layers of a model to the GPU as possible based on available resources, while offload all layers completely is not possible.',
|
||||
'models.form.distribution.tips':
|
||||
'allows for offloading part of the computation to single or multiple remote workers when the resources of a single GPU or worker are insufficient.',
|
||||
'Allows for offloading part of the computation to single or multiple remote workers when the resources of a single GPU or worker are insufficient.',
|
||||
'models.openinplayground': 'Open in Playground',
|
||||
'models.instances': 'instances',
|
||||
'models.table.replicas.edit': 'Edit Replicas',
|
||||
@@ -40,5 +40,13 @@ export default {
|
||||
'models.search.networkerror': 'Network connection exception!',
|
||||
'models.search.hfvisit': 'Please make sure you can visit',
|
||||
'models.search.unsupport':
|
||||
'This model is not supported and may be unusable after deployment.'
|
||||
'This model is not supported and may be unusable after deployment.',
|
||||
'models.form.scheduletype': 'Schedule Type',
|
||||
'models.form.scheduletype.auto': 'Auto',
|
||||
'models.form.scheduletype.manual': 'Manual',
|
||||
'models.form.scheduletype.auto.tips':
|
||||
'Automatically deploys model instances to appropriate GPUs/Workers based on current resource conditions.',
|
||||
'models.form.scheduletype.manual.tips':
|
||||
'Allows you to manually specify the GPUs/Workers to deploy the model instances to.',
|
||||
'models.form.manual.schedule': 'Manual Schedule'
|
||||
};
|
||||
|
||||
@@ -8,17 +8,17 @@ export default {
|
||||
'resources.table.hostname': 'Hostname',
|
||||
'resources.table.key.tips': 'The same key exists.',
|
||||
'resources.form.advanced': 'Advanced',
|
||||
'resources.form.enablePartialOffload': 'Enable Partial Offload',
|
||||
'resources.form.enablePartialOffload': 'Allow Partial Offload',
|
||||
'resources.form.placementStrategy': 'Placement Strategy',
|
||||
'resources.form.workerSelector': 'Matching Worker Labels',
|
||||
'resources.form.workerSelector': 'Worker Selector',
|
||||
'resources.form.enableDistributedInferenceAcrossWorkers':
|
||||
'Enable Distributed Inference Across Workers',
|
||||
'Allow Distributed Inference Across Workers',
|
||||
'resources.form.spread.tips':
|
||||
'Make the resources of the entire cluster relatively evenly distributed among all workers. It may produce more resource fragmentation on a single worker.',
|
||||
'resources.form.binpack.tips':
|
||||
'Prioritize the overall utilization of cluster resources, reducing resource fragmentation on Workers/GPUs.',
|
||||
'resources.form.workerSelector.description':
|
||||
'The scheduling system selects the most suitable GPU or Worker for deploying model instances based on predefined labels.',
|
||||
'The system selects the most suitable GPU or Worker for deploying model instances based on predefined labels.',
|
||||
'resources.table.ip': 'IP',
|
||||
'resources.table.cpu': 'CPU',
|
||||
'resources.table.memory': 'RAM',
|
||||
|
||||
@@ -196,5 +196,6 @@ export default {
|
||||
'common.button.docs': '文档',
|
||||
'common.button.version': '版本',
|
||||
'common.title.delete.confirm': '确认删除',
|
||||
'common.button.addLabel': '添加标签'
|
||||
'common.button.addLabel': '添加标签',
|
||||
'common.button.addSelector': '添加选择器'
|
||||
};
|
||||
|
||||
@@ -39,5 +39,13 @@ export default {
|
||||
'models.search.nofiles': '无可用文件',
|
||||
'models.search.networkerror': '网络连接异常!',
|
||||
'models.search.hfvisit': '请确保您可以访问',
|
||||
'models.search.unsupport': '暂不支持该模型,部署后可能无法使用'
|
||||
'models.search.unsupport': '暂不支持该模型,部署后可能无法使用',
|
||||
'models.form.scheduletype': '调度方式',
|
||||
'models.form.scheduletype.auto': '自动',
|
||||
'models.form.scheduletype.manual': '手动',
|
||||
'models.form.scheduletype.auto.tips':
|
||||
'自动根据当前资源情况部署模型实例到合适的 GPU/Worker。',
|
||||
'models.form.scheduletype.manual.tips':
|
||||
'手动调度可指定模型实例部署的 GPU/Worker。',
|
||||
'models.form.manual.schedule': '手动调度'
|
||||
};
|
||||
|
||||
@@ -8,16 +8,17 @@ export default {
|
||||
'resources.table.key.tips': '存在相同的 key.',
|
||||
'resources.table.labels': '标签',
|
||||
'resources.form.advanced': '高级',
|
||||
'resources.form.enablePartialOffload': '开启半卸载',
|
||||
'resources.form.enablePartialOffload': '允许半卸载',
|
||||
'resources.form.placementStrategy': '放置策略',
|
||||
'resources.form.workerSelector': '匹配的 Worker 标签',
|
||||
'resources.form.enableDistributedInferenceAcrossWorkers': '跨节点分布式推理',
|
||||
'resources.form.workerSelector': 'Worker 选择器',
|
||||
'resources.form.enableDistributedInferenceAcrossWorkers':
|
||||
'允许跨节点分布式推理',
|
||||
'resources.form.spread.tips':
|
||||
'使得集群整体的资源在所有 Worker 之间分配得相对均匀。可能会在单个 Worker 上产生较多资源碎片。',
|
||||
'resources.form.binpack.tips':
|
||||
'优先考虑整体集群的资源最大化利用,减少 Worker/GPU 上的资源碎片。',
|
||||
'resources.form.workerSelector.description':
|
||||
'调度系统在部署模型实例时,会根据预定义的标签来选择最符合要求的 GPU 或 Worker。',
|
||||
'系统在部署模型实例时,会根据预定义的标签来选择最符合要求的 GPU 或 Worker。',
|
||||
'resources.table.ip': 'IP',
|
||||
'resources.table.cpu': 'CPU',
|
||||
'resources.table.memory': '内存',
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import LabelSelector from '@/components/label-selector';
|
||||
import SealAutoComplete from '@/components/seal-form/auto-complete';
|
||||
import FormItemWrapper from '@/components/seal-form/components/wrapper';
|
||||
import SealInput from '@/components/seal-form/seal-input';
|
||||
import SealSelect from '@/components/seal-form/seal-select';
|
||||
import { PageAction } from '@/config';
|
||||
import { PageActionType } from '@/config/types';
|
||||
import { InfoCircleOutlined, RightOutlined } from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Checkbox, Collapse, Form, Tooltip, Typography } from 'antd';
|
||||
import { Checkbox, Collapse, Form, Select, Tooltip, Typography } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import React, {
|
||||
forwardRef,
|
||||
@@ -23,6 +22,7 @@ import {
|
||||
} from '../config';
|
||||
import { FormData } from '../config/types';
|
||||
import dataformStyles from '../style/data-form.less';
|
||||
import GPUCard from './gpu-card';
|
||||
|
||||
interface DataFormProps {
|
||||
ref?: any;
|
||||
@@ -45,12 +45,109 @@ const sourceOptions = [
|
||||
}
|
||||
];
|
||||
|
||||
const gpuOptions = [
|
||||
{
|
||||
label: 'NVIDIA A100',
|
||||
value: '1',
|
||||
index: 0,
|
||||
worker_name: 'mbp-1.local',
|
||||
worker: {
|
||||
GPU: {
|
||||
name: 'A100',
|
||||
count: 1,
|
||||
memory: 16384
|
||||
},
|
||||
CPU: {
|
||||
count: 16,
|
||||
memory: 16384
|
||||
},
|
||||
os: {
|
||||
name: 'Ubuntu 20.04',
|
||||
version: '20.04'
|
||||
},
|
||||
name: 'mbp-1.local',
|
||||
address: '192.168.1.1'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'NVIDIA H100',
|
||||
value: '2',
|
||||
index: 1,
|
||||
worker_name: 'mbp-2.local',
|
||||
worker: {
|
||||
GPU: {
|
||||
name: 'H100',
|
||||
count: 1,
|
||||
memory: 16384
|
||||
},
|
||||
CPU: {
|
||||
count: 16,
|
||||
memory: 16384
|
||||
},
|
||||
os: {
|
||||
name: 'Ubuntu 20.04',
|
||||
version: '20.04'
|
||||
},
|
||||
name: 'mbp-2.local',
|
||||
address: '192.168.1.2'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'NVIDIA A100',
|
||||
value: '3',
|
||||
index: 2,
|
||||
worker_name: 'mbp-3.local',
|
||||
worker: {
|
||||
GPU: {
|
||||
name: 'A100',
|
||||
count: 1,
|
||||
memory: 16384
|
||||
},
|
||||
CPU: {
|
||||
count: 16,
|
||||
memory: 16384
|
||||
},
|
||||
os: {
|
||||
name: 'Ubuntu 20.04',
|
||||
version: '20.04'
|
||||
},
|
||||
name: 'mbp-3.local',
|
||||
address: '192.168.1.3'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'NVIDIA H100',
|
||||
value: '4',
|
||||
index: 3,
|
||||
worker_name: 'mbp-4.local',
|
||||
worker: {
|
||||
GPU: {
|
||||
name: 'H100',
|
||||
count: 1,
|
||||
memory: 16384
|
||||
},
|
||||
CPU: {
|
||||
count: 16,
|
||||
memory: 16384
|
||||
},
|
||||
os: {
|
||||
name: 'Ubuntu 20.04',
|
||||
version: '20.04'
|
||||
},
|
||||
name: 'mbp-4.local',
|
||||
address: '192.168.1.4'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||
const { action, repo, onOk } = props;
|
||||
const [form] = Form.useForm();
|
||||
const intl = useIntl();
|
||||
const wokerSelector = Form.useWatch('worker_selector', form);
|
||||
const [scheduleType, setScheduleType] = React.useState('auto');
|
||||
// const [scheduleType, setScheduleType] = React.useState(false); // false: auto, true: manual
|
||||
|
||||
const scheduleType = Form.useWatch('scheduleType', form);
|
||||
|
||||
const handleSumit = () => {
|
||||
form.submit();
|
||||
@@ -75,6 +172,40 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||
[]
|
||||
);
|
||||
|
||||
const placementStrategyTips = [
|
||||
{
|
||||
title: 'Spread',
|
||||
tips: intl.formatMessage({
|
||||
id: 'resources.form.spread.tips'
|
||||
})
|
||||
},
|
||||
{
|
||||
title: 'Binpack',
|
||||
tips: intl.formatMessage({
|
||||
id: 'resources.form.binpack.tips'
|
||||
})
|
||||
}
|
||||
];
|
||||
|
||||
const scheduleTypeTips = [
|
||||
{
|
||||
title: intl.formatMessage({
|
||||
id: 'models.form.scheduletype.auto'
|
||||
}),
|
||||
tips: intl.formatMessage({
|
||||
id: 'models.form.scheduletype.auto.tips'
|
||||
})
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({
|
||||
id: 'models.form.scheduletype.manual'
|
||||
}),
|
||||
tips: intl.formatMessage({
|
||||
id: 'models.form.scheduletype.manual.tips'
|
||||
})
|
||||
}
|
||||
];
|
||||
|
||||
const handleOnSelectModel = () => {
|
||||
console.log('repo=============', repo);
|
||||
if (!repo) {
|
||||
@@ -224,33 +355,53 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||
}
|
||||
};
|
||||
|
||||
const renderSelectTips = (list: Array<{ title: string; tips: string }>) => {
|
||||
return (
|
||||
<div>
|
||||
{list.map((item, index) => {
|
||||
return (
|
||||
<div className="m-b-8" key={index}>
|
||||
<Typography.Title
|
||||
level={5}
|
||||
style={{
|
||||
color: 'var(--color-white-1)',
|
||||
marginRight: 10
|
||||
}}
|
||||
>
|
||||
{item.title}:
|
||||
</Typography.Title>
|
||||
<Typography.Text style={{ color: 'var(--color-white-1)' }}>
|
||||
{item.tips}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const collapseItems = useMemo(() => {
|
||||
const children = (
|
||||
<>
|
||||
<Form.Item<FormData>
|
||||
name="replicas"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage(
|
||||
{
|
||||
id: 'common.form.rule.input'
|
||||
},
|
||||
{
|
||||
name: intl.formatMessage({ id: 'models.form.replicas' })
|
||||
}
|
||||
)
|
||||
}
|
||||
]}
|
||||
>
|
||||
<SealInput.Number
|
||||
style={{ width: '100%' }}
|
||||
label={intl.formatMessage({
|
||||
id: 'models.form.replicas'
|
||||
})}
|
||||
required
|
||||
min={0}
|
||||
></SealInput.Number>
|
||||
<Form.Item name="scheduleType">
|
||||
<SealSelect
|
||||
label={intl.formatMessage({ id: 'models.form.scheduletype' })}
|
||||
description={renderSelectTips(scheduleTypeTips)}
|
||||
options={[
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'models.form.scheduletype.auto'
|
||||
}),
|
||||
value: 'auto'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'models.form.scheduletype.manual'
|
||||
}),
|
||||
value: 'manual'
|
||||
}
|
||||
]}
|
||||
></SealSelect>
|
||||
</Form.Item>
|
||||
{scheduleType === 'auto' && (
|
||||
<Form.Item<FormData> name="placement_strategy">
|
||||
@@ -259,98 +410,10 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||
id: 'resources.form.placementStrategy'
|
||||
})}
|
||||
options={placementStrategyOptions}
|
||||
description={
|
||||
<div>
|
||||
<div className="m-b-8">
|
||||
<Typography.Title
|
||||
level={5}
|
||||
style={{
|
||||
color: 'var(--color-white-1)',
|
||||
marginRight: 10
|
||||
}}
|
||||
>
|
||||
Spread:
|
||||
</Typography.Title>
|
||||
<Typography.Text style={{ color: 'var(--color-white-1)' }}>
|
||||
{intl.formatMessage({
|
||||
id: 'resources.form.spread.tips'
|
||||
})}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
<div>
|
||||
<Typography.Title
|
||||
level={5}
|
||||
style={{ color: 'var(--color-white-1)', marginRight: 10 }}
|
||||
>
|
||||
Binpack:
|
||||
</Typography.Title>
|
||||
<Typography.Text style={{ color: 'var(--color-white-1)' }}>
|
||||
{intl.formatMessage({
|
||||
id: 'resources.form.binpack.tips'
|
||||
})}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
description={renderSelectTips(placementStrategyTips)}
|
||||
></SealSelect>
|
||||
</Form.Item>
|
||||
)}
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<FormItemWrapper noWrapperStyle>
|
||||
<Form.Item<FormData>
|
||||
name="partial_offload"
|
||||
valuePropName="checked"
|
||||
style={{ padding: '0 10px', marginBottom: 0 }}
|
||||
>
|
||||
<Checkbox>
|
||||
<Tooltip
|
||||
title={intl.formatMessage({
|
||||
id: 'models.form.partialoffload.tips'
|
||||
})}
|
||||
>
|
||||
<span style={{ color: 'var(--ant-color-text-tertiary)' }}>
|
||||
{intl.formatMessage({
|
||||
id: 'resources.form.enablePartialOffload'
|
||||
})}
|
||||
</span>
|
||||
<InfoCircleOutlined
|
||||
className="m-l-4"
|
||||
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Checkbox>
|
||||
</Form.Item>
|
||||
</FormItemWrapper>
|
||||
</div>
|
||||
{scheduleType === 'auto' && (
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<FormItemWrapper noWrapperStyle>
|
||||
<Form.Item<FormData>
|
||||
name="distributed_inference_across_workers"
|
||||
valuePropName="checked"
|
||||
style={{ padding: '0 10px', marginBottom: 0 }}
|
||||
>
|
||||
<Checkbox>
|
||||
<Tooltip
|
||||
title={intl.formatMessage({
|
||||
id: 'models.form.distribution.tips'
|
||||
})}
|
||||
>
|
||||
<span style={{ color: 'var(--ant-color-text-tertiary)' }}>
|
||||
{intl.formatMessage({
|
||||
id: 'resources.form.enableDistributedInferenceAcrossWorkers'
|
||||
})}
|
||||
</span>
|
||||
<InfoCircleOutlined
|
||||
className="m-l-4"
|
||||
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Checkbox>
|
||||
</Form.Item>
|
||||
</FormItemWrapper>
|
||||
</div>
|
||||
)}
|
||||
{scheduleType === 'auto' && (
|
||||
<Form.Item<FormData> name="worker_selector">
|
||||
<LabelSelector
|
||||
@@ -369,6 +432,71 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||
></LabelSelector>
|
||||
</Form.Item>
|
||||
)}
|
||||
{scheduleType === 'manual' && (
|
||||
<Form.Item<FormData> name="gpu_selector">
|
||||
<SealSelect label="GPU Selector">
|
||||
{gpuOptions.map((item) => (
|
||||
<Select.Option key={item.value} value={item.value}>
|
||||
<GPUCard data={item}></GPUCard>
|
||||
</Select.Option>
|
||||
))}
|
||||
</SealSelect>
|
||||
</Form.Item>
|
||||
)}
|
||||
<div style={{ marginBottom: 22, paddingLeft: 10 }}>
|
||||
<Form.Item<FormData>
|
||||
name="partial_offload"
|
||||
valuePropName="checked"
|
||||
style={{ padding: '0 10px', marginBottom: 0 }}
|
||||
noStyle
|
||||
>
|
||||
<Checkbox className="p-l-6">
|
||||
<Tooltip
|
||||
title={intl.formatMessage({
|
||||
id: 'models.form.partialoffload.tips'
|
||||
})}
|
||||
>
|
||||
<span style={{ color: 'var(--ant-color-text-tertiary)' }}>
|
||||
{intl.formatMessage({
|
||||
id: 'resources.form.enablePartialOffload'
|
||||
})}
|
||||
</span>
|
||||
<InfoCircleOutlined
|
||||
className="m-l-4"
|
||||
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Checkbox>
|
||||
</Form.Item>
|
||||
</div>
|
||||
{scheduleType === 'auto' && (
|
||||
<div style={{ marginBottom: 22, paddingLeft: 10 }}>
|
||||
<Form.Item<FormData>
|
||||
name="distributed_inference_across_workers"
|
||||
valuePropName="checked"
|
||||
style={{ padding: '0 10px', marginBottom: 0 }}
|
||||
noStyle
|
||||
>
|
||||
<Checkbox className="p-l-6">
|
||||
<Tooltip
|
||||
title={intl.formatMessage({
|
||||
id: 'models.form.distribution.tips'
|
||||
})}
|
||||
>
|
||||
<span style={{ color: 'var(--ant-color-text-tertiary)' }}>
|
||||
{intl.formatMessage({
|
||||
id: 'resources.form.enableDistributedInferenceAcrossWorkers'
|
||||
})}
|
||||
</span>
|
||||
<InfoCircleOutlined
|
||||
className="m-l-4"
|
||||
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Checkbox>
|
||||
</Form.Item>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
return [
|
||||
@@ -401,6 +529,7 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||
source: props.source,
|
||||
placement_strategy: 'spread',
|
||||
partial_offload: true,
|
||||
scheduleType: 'auto',
|
||||
distributed_inference_across_workers: true
|
||||
}}
|
||||
>
|
||||
@@ -451,6 +580,31 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||
}
|
||||
</Form.Item>
|
||||
{renderFieldsBySource()}
|
||||
<Form.Item<FormData>
|
||||
name="replicas"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage(
|
||||
{
|
||||
id: 'common.form.rule.input'
|
||||
},
|
||||
{
|
||||
name: intl.formatMessage({ id: 'models.form.replicas' })
|
||||
}
|
||||
)
|
||||
}
|
||||
]}
|
||||
>
|
||||
<SealInput.Number
|
||||
style={{ width: '100%' }}
|
||||
label={intl.formatMessage({
|
||||
id: 'models.form.replicas'
|
||||
})}
|
||||
required
|
||||
min={0}
|
||||
></SealInput.Number>
|
||||
</Form.Item>
|
||||
<Form.Item<FormData> name="description">
|
||||
<SealInput.TextArea
|
||||
label={intl.formatMessage({
|
||||
@@ -467,7 +621,7 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||
expandIcon={({ isActive }) => (
|
||||
<RightOutlined
|
||||
rotate={isActive ? 90 : 0}
|
||||
style={{ fontSize: '12px' }}
|
||||
style={{ fontSize: '12px', marginLeft: -8 }}
|
||||
/>
|
||||
)}
|
||||
items={collapseItems}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { convertFileSize } from '@/utils';
|
||||
import _ from 'lodash';
|
||||
import React from 'react';
|
||||
import '../style/gpu-card.less';
|
||||
|
||||
const GPUCard: React.FC<{
|
||||
data: any;
|
||||
}> = ({ data }) => {
|
||||
return (
|
||||
<div className="gpu-card">
|
||||
<div className="header">
|
||||
{data.label}({data.worker_name})[Index:{data.index}]
|
||||
</div>
|
||||
<div className="info">
|
||||
<span>VRAM:</span>
|
||||
<span>Total {convertFileSize(22906503168)}</span>
|
||||
<span>Used {convertFileSize(3322640640)}</span>
|
||||
<span>Utilization {_.round(3.79, 2)}%</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GPUCard;
|
||||
@@ -294,31 +294,6 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
|
||||
),
|
||||
children: (
|
||||
<>
|
||||
<Form.Item<FormData>
|
||||
name="replicas"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage(
|
||||
{
|
||||
id: 'common.form.rule.input'
|
||||
},
|
||||
{
|
||||
name: intl.formatMessage({ id: 'models.form.replicas' })
|
||||
}
|
||||
)
|
||||
}
|
||||
]}
|
||||
>
|
||||
<SealInput.Number
|
||||
style={{ width: '100%' }}
|
||||
label={intl.formatMessage({
|
||||
id: 'models.form.replicas'
|
||||
})}
|
||||
required
|
||||
min={0}
|
||||
></SealInput.Number>
|
||||
</Form.Item>
|
||||
<Form.Item<FormData> name="placement_strategy">
|
||||
<SealSelect
|
||||
label={intl.formatMessage({
|
||||
@@ -528,6 +503,31 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
|
||||
)}
|
||||
</Form.Item>
|
||||
{renderFieldsBySource()}
|
||||
<Form.Item<FormData>
|
||||
name="replicas"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage(
|
||||
{
|
||||
id: 'common.form.rule.input'
|
||||
},
|
||||
{
|
||||
name: intl.formatMessage({ id: 'models.form.replicas' })
|
||||
}
|
||||
)
|
||||
}
|
||||
]}
|
||||
>
|
||||
<SealInput.Number
|
||||
style={{ width: '100%' }}
|
||||
label={intl.formatMessage({
|
||||
id: 'models.form.replicas'
|
||||
})}
|
||||
required
|
||||
min={0}
|
||||
></SealInput.Number>
|
||||
</Form.Item>
|
||||
<Form.Item<FormData> name="description">
|
||||
<SealInput.TextArea
|
||||
label={intl.formatMessage({
|
||||
|
||||
@@ -25,6 +25,7 @@ export interface FormData {
|
||||
placement_strategy?: string;
|
||||
partial_offload?: boolean;
|
||||
worker_selector?: object;
|
||||
scheduleType?: string;
|
||||
name: string;
|
||||
replicas: number;
|
||||
description: string;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
.ant-collapse-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px !important;
|
||||
margin-bottom: 10px !important;
|
||||
padding-inline: 5px !important;
|
||||
padding-block: 5px !important;
|
||||
border-radius: var(--border-radius-base) !important;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
.gpu-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid var(--ant-color-split);
|
||||
padding-bottom: 10px;
|
||||
|
||||
.header {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
color: var(--ant-color-text-tertiary);
|
||||
}
|
||||
}
|
||||
@@ -148,7 +148,7 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
|
||||
onFinishFailed={handleOnFinishFailed}
|
||||
>
|
||||
<div>
|
||||
<h3 className="m-b-20 m-l-10 font-size-14">
|
||||
<h3 className="m-b-15 m-l-10 font-size-14">
|
||||
{intl.formatMessage({ id: 'playground.model' })}
|
||||
</h3>
|
||||
<Form.Item<ParamsSettingsFormProps>
|
||||
@@ -165,11 +165,7 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
|
||||
}
|
||||
]}
|
||||
>
|
||||
<SealSelect
|
||||
showSearch
|
||||
options={ModelList}
|
||||
label={intl.formatMessage({ id: 'playground.model' })}
|
||||
></SealSelect>
|
||||
<SealSelect showSearch options={ModelList}></SealSelect>
|
||||
</Form.Item>
|
||||
<h3 className="m-b-20 m-l-10 flex-between flex-center font-size-14">
|
||||
<span>{intl.formatMessage({ id: 'playground.parameters' })}</span>
|
||||
|
||||
Reference in New Issue
Block a user