chore: worker downloading tooltip
This commit is contained in:
@@ -39,6 +39,34 @@ const fieldList = [
|
||||
}
|
||||
];
|
||||
|
||||
const downloadList: ColumnProps[] = [
|
||||
{
|
||||
title: 'Worker',
|
||||
key: 'worker_name',
|
||||
width: 200
|
||||
},
|
||||
{
|
||||
title: 'Status',
|
||||
key: 'download_progress',
|
||||
render: ({ row }) => {
|
||||
return (
|
||||
<StatusTag
|
||||
download={{
|
||||
percent: row.download_progress
|
||||
}}
|
||||
statusValue={{
|
||||
status: row.download_progress
|
||||
? status[InstanceStatusMap.Running]
|
||||
: status[InstanceStatusMap.Initializing],
|
||||
text: row.download_progress,
|
||||
message: ''
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const WorkerInfo = (props: {
|
||||
title: React.ReactNode;
|
||||
defaultOpen: boolean;
|
||||
@@ -70,6 +98,82 @@ const WorkerInfo = (props: {
|
||||
);
|
||||
};
|
||||
|
||||
const RenderRayactorDownloading = (props: {
|
||||
severList: any[];
|
||||
instanceData: any;
|
||||
workerList: WorkerListItem[];
|
||||
}) => {
|
||||
const { severList, instanceData, workerList } = props;
|
||||
if (!severList.length) {
|
||||
return null;
|
||||
}
|
||||
const list = _.map(severList, (item: any) => {
|
||||
const data = _.find(workerList, { id: item.worker_id });
|
||||
return {
|
||||
worker_name: data?.name,
|
||||
worker_ip: data?.ip,
|
||||
download_progress: _.round(item.download_progress, 2)
|
||||
};
|
||||
});
|
||||
|
||||
const mainWorker = [
|
||||
{
|
||||
worker_name: `${instanceData.worker_name}`,
|
||||
worker_ip: `${instanceData.worker_ip}`,
|
||||
download_progress: _.round(instanceData.download_progress, 2)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<SimpleTabel
|
||||
columns={downloadList}
|
||||
dataSource={[...mainWorker, ...list]}
|
||||
theme="light"
|
||||
></SimpleTabel>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const RenderWorkerDownloading = (props: {
|
||||
rayActors: any[];
|
||||
workerList: WorkerListItem[];
|
||||
instanceData: ModelInstanceListItem;
|
||||
}) => {
|
||||
const { rayActors, workerList, instanceData } = props;
|
||||
if (instanceData.state === InstanceStatusMap.Error || !rayActors.length) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Tooltip
|
||||
arrow={true}
|
||||
overlayInnerStyle={{
|
||||
width: 300,
|
||||
backgroundColor: 'var(--color-white-1)'
|
||||
}}
|
||||
overlayClassName="light-downloading-tooltip"
|
||||
title={
|
||||
<RenderRayactorDownloading
|
||||
severList={rayActors}
|
||||
workerList={workerList}
|
||||
instanceData={instanceData}
|
||||
></RenderRayactorDownloading>
|
||||
}
|
||||
>
|
||||
<Progress
|
||||
showInfo={false}
|
||||
type="circle"
|
||||
size={20}
|
||||
strokeColor="var(--color-progress-green)"
|
||||
percent={
|
||||
_.find(rayActors, (item: any) => item.download_progress < 100)
|
||||
?.download_progress || 100
|
||||
}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const InstanceStatusTag = (
|
||||
props: Pick<InstanceItemProps, 'instanceData' | 'handleChildSelect'>
|
||||
) => {
|
||||
@@ -79,39 +183,41 @@ const InstanceStatusTag = (
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<StatusTag
|
||||
download={
|
||||
instanceData.state === InstanceStatusMap.Downloading
|
||||
? { percent: instanceData.download_progress }
|
||||
: undefined
|
||||
}
|
||||
extra={
|
||||
instanceData.state === InstanceStatusMap.Error &&
|
||||
instanceData.worker_id ? (
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
style={{ paddingLeft: 0 }}
|
||||
onClick={() => handleChildSelect('viewlog', instanceData)}
|
||||
>
|
||||
{intl.formatMessage({ id: 'models.list.more.logs' })}
|
||||
</Button>
|
||||
) : null
|
||||
}
|
||||
statusValue={{
|
||||
status:
|
||||
instanceData.state === InstanceStatusMap.Downloading &&
|
||||
instanceData.download_progress === 100
|
||||
? status[InstanceStatusMap.Running]
|
||||
: status[instanceData.state],
|
||||
text: InstanceStatusMapValue[instanceData.state],
|
||||
message:
|
||||
instanceData.state === InstanceStatusMap.Downloading &&
|
||||
instanceData.download_progress === 100
|
||||
? ''
|
||||
: instanceData.state_message
|
||||
}}
|
||||
/>
|
||||
<>
|
||||
<StatusTag
|
||||
download={
|
||||
instanceData.state === InstanceStatusMap.Downloading
|
||||
? { percent: instanceData.download_progress }
|
||||
: undefined
|
||||
}
|
||||
extra={
|
||||
instanceData.state === InstanceStatusMap.Error &&
|
||||
instanceData.worker_id ? (
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
style={{ paddingLeft: 0 }}
|
||||
onClick={() => handleChildSelect('viewlog', instanceData)}
|
||||
>
|
||||
{intl.formatMessage({ id: 'models.list.more.logs' })}
|
||||
</Button>
|
||||
) : null
|
||||
}
|
||||
statusValue={{
|
||||
status:
|
||||
instanceData.state === InstanceStatusMap.Downloading &&
|
||||
instanceData.download_progress === 100
|
||||
? status[InstanceStatusMap.Running]
|
||||
: status[instanceData.state],
|
||||
text: InstanceStatusMapValue[instanceData.state],
|
||||
message:
|
||||
instanceData.state === InstanceStatusMap.Downloading &&
|
||||
instanceData.download_progress === 100
|
||||
? ''
|
||||
: instanceData.state_message
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -341,6 +447,7 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
|
||||
<Tag
|
||||
color="processing"
|
||||
style={{
|
||||
marginRight: 0,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
maxWidth: '100%',
|
||||
@@ -471,13 +578,18 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<span
|
||||
style={{ paddingLeft: '62px' }}
|
||||
className="flex justify-center"
|
||||
style={{ paddingLeft: '62px', gap: 4 }}
|
||||
className="flex-center justify-center"
|
||||
>
|
||||
<InstanceStatusTag
|
||||
instanceData={instanceData}
|
||||
handleChildSelect={handleChildSelect}
|
||||
/>
|
||||
<RenderWorkerDownloading
|
||||
rayActors={instanceData.distributed_servers?.ray_actors || []}
|
||||
workerList={workerList}
|
||||
instanceData={instanceData}
|
||||
></RenderWorkerDownloading>
|
||||
</span>
|
||||
</Col>
|
||||
<Col span={5}>
|
||||
|
||||
@@ -6,6 +6,64 @@ import { ModelInstanceListItem } from '../config/types';
|
||||
import '../style/instance-item.less';
|
||||
import InstanceItem from './instance-item';
|
||||
|
||||
const testInstanceData = {
|
||||
source: 'huggingface',
|
||||
huggingface_repo_id: 'Qwen/Qwen2.5-3B-Instruct',
|
||||
huggingface_filename: null,
|
||||
ollama_library_model_name: null,
|
||||
model_scope_model_id: null,
|
||||
model_scope_file_path: null,
|
||||
local_path: null,
|
||||
name: 'qwen2.5-XVCXa',
|
||||
worker_id: 1,
|
||||
worker_name: 'sealgpuhost4080',
|
||||
worker_ip: '192.168.50.12',
|
||||
pid: 208852,
|
||||
port: 40063,
|
||||
download_progress: 72.11,
|
||||
resolved_path: null,
|
||||
state: 'downloading',
|
||||
state_message: '',
|
||||
computed_resource_claim: {
|
||||
is_unified_memory: false,
|
||||
offload_layers: null,
|
||||
total_layers: null,
|
||||
ram: null,
|
||||
vram: {
|
||||
'0': 1717148058
|
||||
},
|
||||
tensor_split: null
|
||||
},
|
||||
gpu_indexes: [0],
|
||||
model_id: 1,
|
||||
model_name: 'qwen2.5',
|
||||
distributed_servers: {
|
||||
rpc_servers: null,
|
||||
ray_actors: [
|
||||
{
|
||||
worker_id: 2,
|
||||
worker_ip: '192.168.50.13',
|
||||
total_gpus: 1,
|
||||
gpu_indexes: [0],
|
||||
computed_resource_claim: {
|
||||
is_unified_memory: false,
|
||||
offload_layers: null,
|
||||
total_layers: null,
|
||||
ram: null,
|
||||
vram: {
|
||||
'0': 23181498777
|
||||
},
|
||||
tensor_split: null
|
||||
},
|
||||
download_progress: 27.49
|
||||
}
|
||||
]
|
||||
},
|
||||
id: 1,
|
||||
created_at: '2025-03-24T12:06:30Z',
|
||||
updated_at: '2025-03-24T12:09:01Z'
|
||||
};
|
||||
|
||||
interface InstanceItemProps {
|
||||
list: ModelInstanceListItem[];
|
||||
workerList: WorkerListItem[];
|
||||
|
||||
@@ -12,7 +12,6 @@ import useBodyScroll from '@/hooks/use-body-scroll';
|
||||
import useTableFetch from '@/hooks/use-table-fetch';
|
||||
import { createModel } from '@/pages/llmodels/apis';
|
||||
import DeployModal from '@/pages/llmodels/components/deploy-modal';
|
||||
import FileParts from '@/pages/llmodels/components/file-parts';
|
||||
import {
|
||||
backendOptionsMap,
|
||||
getSourceRepoConfigValue,
|
||||
@@ -27,22 +26,17 @@ import {
|
||||
} from '@/pages/llmodels/config/button-actions';
|
||||
import DownloadModal from '@/pages/llmodels/download';
|
||||
import { convertFileSize } from '@/utils';
|
||||
import {
|
||||
DeleteOutlined,
|
||||
DownOutlined,
|
||||
InfoCircleOutlined,
|
||||
SyncOutlined
|
||||
} from '@ant-design/icons';
|
||||
import { DeleteOutlined, DownOutlined, SyncOutlined } from '@ant-design/icons';
|
||||
import { useIntl, useNavigate } from '@umijs/max';
|
||||
import {
|
||||
Button,
|
||||
ConfigProvider,
|
||||
Empty,
|
||||
Input,
|
||||
Select,
|
||||
Space,
|
||||
Table,
|
||||
Tag,
|
||||
Tooltip,
|
||||
message
|
||||
} from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
@@ -112,7 +106,7 @@ const InstanceStatusTag = (props: { data: ListItem }) => {
|
||||
};
|
||||
|
||||
const ModelFiles = () => {
|
||||
const { getGPUList, generateFormValues } = useGenerateFormEditInitialValues();
|
||||
const { getGPUList } = useGenerateFormEditInitialValues();
|
||||
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
|
||||
const [modelsExpandKeys, setModelsExpandKeys] = useAtom(modelsExpandKeysAtom);
|
||||
const navigate = useNavigate();
|
||||
@@ -127,7 +121,8 @@ const ModelFiles = () => {
|
||||
handlePageChange,
|
||||
handleTableChange,
|
||||
handleSearch,
|
||||
handleNameChange
|
||||
handleNameChange,
|
||||
setQueryParams
|
||||
} = useTableFetch<ListItem>({
|
||||
fetchAPI: queryModelFilesList,
|
||||
deleteAPI: deleteModelFile,
|
||||
@@ -202,6 +197,20 @@ const ModelFiles = () => {
|
||||
return name.replace(filterPattern, '$1');
|
||||
};
|
||||
|
||||
const handleWorkerChange = (value: number) => {
|
||||
setQueryParams({
|
||||
...queryParams,
|
||||
page: 1,
|
||||
worker_id: value
|
||||
});
|
||||
fetchData({
|
||||
query: {
|
||||
...queryParams,
|
||||
page: 1,
|
||||
worker_id: value
|
||||
}
|
||||
});
|
||||
};
|
||||
const generateInitialValues = (record: ListItem) => {
|
||||
const isGGUF = _.includes(record.resolved_paths?.[0], 'gguf');
|
||||
const isOllama = !!record.ollama_library_model_name;
|
||||
@@ -251,28 +260,20 @@ const ModelFiles = () => {
|
||||
};
|
||||
});
|
||||
return (
|
||||
<Tooltip
|
||||
overlayInnerStyle={{
|
||||
width: 120,
|
||||
padding: 0
|
||||
<Tag
|
||||
className="flex-center"
|
||||
color="purple"
|
||||
style={{
|
||||
marginRight: 0,
|
||||
height: 22,
|
||||
borderRadius: 'var(--border-radius-base)'
|
||||
}}
|
||||
title={<FileParts fileList={partsList} showSize={false}></FileParts>}
|
||||
>
|
||||
<Tag
|
||||
className="tag-item"
|
||||
color="purple"
|
||||
style={{
|
||||
marginRight: 0,
|
||||
height: 22,
|
||||
borderRadius: 'var(--border-radius-base)'
|
||||
}}
|
||||
>
|
||||
<span style={{ opacity: 1 }}>
|
||||
<InfoCircleOutlined className="m-r-5" />
|
||||
{partsList.length} parts
|
||||
</span>
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
<span style={{ opacity: 1 }}>
|
||||
{record.resolved_paths?.length}{' '}
|
||||
{intl.formatMessage({ id: 'models.form.files' })}
|
||||
</span>
|
||||
</Tag>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -492,12 +493,23 @@ const ModelFiles = () => {
|
||||
<Space>
|
||||
<Input
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'common.filter.name'
|
||||
id: 'resources.filter.file'
|
||||
})}
|
||||
style={{ width: 300 }}
|
||||
style={{ width: 230 }}
|
||||
allowClear
|
||||
onChange={handleNameChange}
|
||||
></Input>
|
||||
<Select
|
||||
allowClear
|
||||
showSearch={false}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'resources.filter.worker'
|
||||
})}
|
||||
style={{ width: 230 }}
|
||||
size="large"
|
||||
onChange={handleWorkerChange}
|
||||
options={workersList}
|
||||
></Select>
|
||||
<Button
|
||||
type="text"
|
||||
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
||||
|
||||
Reference in New Issue
Block a user