fix: gpu selector by cluster

This commit is contained in:
jialin
2025-09-16 11:26:17 +08:00
parent 207c90e26c
commit 553ec724c2
40 changed files with 1106 additions and 1233 deletions
+4 -2
View File
@@ -5,9 +5,11 @@ export const WORKERS_API = '/workers';
export const GPU_DEVICES_API = '/gpu-devices';
export const MODEL_FILES_API = '/model-files';
export async function queryWorkersList(params: Global.SearchParams) {
export async function queryWorkersList<T extends Record<string, any>>(
params: Global.SearchParams & T
) {
return request<Global.PageResponse<ListItem>>(`${WORKERS_API}`, {
methos: 'GET',
method: 'GET',
params
});
}
@@ -5,12 +5,16 @@ import ContainerInstall from './container-install';
type ViewModalProps = {
open: boolean;
token: string;
registrationInfo: {
token: string;
image: string;
server_url: string;
};
onCancel: () => void;
};
const AddWorker: React.FC<ViewModalProps> = (props) => {
const { open, onCancel, token = '${token}' } = props || {};
const { open, onCancel, registrationInfo } = props || {};
const intl = useIntl();
return (
@@ -30,7 +34,7 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
maxContentHeight={450}
footer={null}
>
<ContainerInstall token={token} />
<ContainerInstall registrationInfo={registrationInfo} />
</ScrollerModal>
);
};
@@ -9,7 +9,11 @@ import { addWorkerGuide, containerInstallOptions } from '../config';
import './styles/installation.less';
type ViewModalProps = {
token: string;
registrationInfo: {
token: string;
image: string;
server_url: string;
};
};
const npuOptions = [
@@ -18,6 +22,7 @@ const npuOptions = [
];
const AddWorker: React.FC<ViewModalProps> = (props) => {
const { registrationInfo } = props || {};
const intl = useIntl();
const origin = window.location.origin;
@@ -40,12 +45,18 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
const tag = activeKey === 'cuda' ? version : `${version}-${activeKey}`;
return commandCode?.registerWorker({
server: origin,
server: registrationInfo.server_url || origin,
tag: tag,
token: props.token || '${token}',
token: registrationInfo.token || '${token}',
workerip: '${workerip}'
});
}, [versionInfo, activeKey, props.token, npuKey]);
}, [
versionInfo,
activeKey,
registrationInfo.token,
registrationInfo.server_url,
npuKey
]);
const handleOnChange = (value: string | number) => {
setNpuKey(value as string);
@@ -53,7 +64,7 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
return (
<div className="container-install">
<h3 className="m-t-10 font-size-14 font-600">
<h3 className="m-t-0 font-size-14 font-600">
1. {intl.formatMessage({ id: 'resources.worker.add.step2' })}{' '}
<span
className="font-size-12"
+22 -64
View File
@@ -20,6 +20,7 @@ import {
} from '@/pages/llmodels/config/button-actions';
import { SourceType } from '@/pages/llmodels/config/types';
import DownloadModal from '@/pages/llmodels/download';
import { useGenerateWorkerOptions } from '@/pages/llmodels/hooks/use-form-initial-values';
import { convertFileSize } from '@/utils';
import {
CheckCircleFilled,
@@ -32,19 +33,14 @@ import { ConfigProvider, Empty, Table, Tag, Typography, message } from 'antd';
import dayjs from 'dayjs';
import { useAtom } from 'jotai';
import _ from 'lodash';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import styled from 'styled-components';
import {
checkCurrentbackend,
useGenerateFormEditInitialValues,
useGenerateModelFileOptions
} from '../../llmodels/hooks';
import { checkCurrentbackend } from '../../llmodels/hooks';
import {
MODEL_FILES_API,
deleteModelFile,
downloadModelFile,
queryModelFilesList,
queryWorkersList,
retryDownloadModelFile
} from '../apis';
import {
@@ -53,10 +49,7 @@ import {
ModelfileStateMapValue,
WorkerStatusMap
} from '../config';
import {
ModelFile as ListItem,
ListItem as WorkerListItem
} from '../config/types';
import { ModelFile as ListItem } from '../config/types';
const { Paragraph } = Typography;
@@ -287,7 +280,8 @@ const ResolvedPathColumn = (props: { record: ListItem }) => {
};
const ModelFiles = () => {
const { getGPUList } = useGenerateFormEditInitialValues();
const { getWorkerOptionList, workerOptions, clusterList, workersList } =
useGenerateWorkerOptions();
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
const [modelsExpandKeys, setModelsExpandKeys] = useAtom(modelsExpandKeysAtom);
const navigate = useNavigate();
@@ -311,63 +305,36 @@ const ModelFiles = () => {
watch: true,
contentForDelete: 'resources.modelfiles.modelfile'
});
const { getModelFileList, generateModelFileOptions } =
useGenerateModelFileOptions();
const intl = useIntl();
const { showSuccess } = useAppUtils();
const [workersList, setWorkersList] = useState<any[]>([]);
const [downloadModalStatus, setDownlaodMoalStatus] = useState<{
show: boolean;
width: number | string;
source: string;
hasLinuxWorker: boolean;
gpuOptions: any[];
}>({
show: false,
width: 600,
hasLinuxWorker: false,
source: modelSourceMap.huggingface_value,
gpuOptions: []
source: modelSourceMap.huggingface_value
});
const [openDeployModal, setOpenDeployModal] = useState<{
show: boolean;
width: number | string;
source: SourceType;
gpuOptions: any[];
modelFileOptions?: any[];
initialValues: any;
isGGUF?: boolean;
}>({
show: false,
width: 600,
source: modelSourceMap.local_path_value as SourceType,
gpuOptions: [],
modelFileOptions: [],
initialValues: {},
isGGUF: false
});
useEffect(() => {
const fetchWorkerList = async () => {
try {
const res = await queryWorkersList({
page: 1,
perPage: 100
});
const list = res.items?.map((item: WorkerListItem) => {
return {
...item,
value: item.id,
label: item.name
};
});
setWorkersList(list);
} catch (error) {
// console.log('error', error);
}
};
fetchWorkerList();
getWorkerOptionList();
}, []);
const extractFileName = (name: string) => {
@@ -440,16 +407,10 @@ const ModelFiles = () => {
showSuccess();
} else if (val === 'deploy') {
saveScrollHeight();
const [modelFileList, gpuList] = await Promise.all([
getModelFileList(),
getGPUList()
]);
const dataList = generateModelFileOptions(modelFileList, workersList);
const initialValues = generateInitialValues(record, gpuList);
const initialValues = generateInitialValues(record, []);
setOpenDeployModal({
...openDeployModal,
modelFileOptions: dataList,
gpuOptions: gpuList,
initialValues: initialValues,
isGGUF: initialValues.isGGUF,
show: true
@@ -472,18 +433,15 @@ const ModelFiles = () => {
return <div></div>;
};
const handleClickDropdown = useCallback(
(item: any) => {
const config = modalConfig[item.key];
const hasLinuxWorker = workersList.some(
(worker) => _.toLower(worker.labels?.os) === 'linux'
);
if (config) {
setDownlaodMoalStatus({ ...config, hasLinuxWorker, gpuOptions: [] });
}
},
[workersList]
);
const handleClickDropdown = (item: any) => {
const config = modalConfig[item.key];
const hasLinuxWorker = workersList.some(
(worker) => _.toLower(worker.labels?.os) === 'linux'
);
if (config) {
setDownlaodMoalStatus({ ...config, hasLinuxWorker });
}
};
const handleDownloadCancel = () => {
setDownlaodMoalStatus({
@@ -709,6 +667,7 @@ const ModelFiles = () => {
width={downloadModalStatus.width}
hasLinuxWorker={downloadModalStatus.hasLinuxWorker}
workersList={readyWorkers}
workerOptions={workerOptions}
></DownloadModal>
<DeployModal
deploymentType="modelFiles"
@@ -719,10 +678,9 @@ const ModelFiles = () => {
action={PageAction.CREATE}
source={openDeployModal.source}
width={openDeployModal.width}
gpuOptions={openDeployModal.gpuOptions}
modelFileOptions={openDeployModal.modelFileOptions || []}
initialValues={openDeployModal.initialValues}
isGGUF={openDeployModal.isGGUF}
clusterList={clusterList}
></DeployModal>
</PageContainer>
</>
+68 -27
View File
@@ -7,6 +7,7 @@ import ProgressBar from '@/components/progress-bar';
import InfoColumn from '@/components/simple-table/info-column';
import StatusTag from '@/components/status-tag';
import useTableFetch from '@/hooks/use-table-fetch';
import { queryClusterList } from '@/pages/cluster-management/apis';
import { convertFileSize } from '@/utils';
import {
CodeOutlined,
@@ -18,7 +19,7 @@ import { PageContainer } from '@ant-design/pro-components';
import { useIntl } from '@umijs/max';
import { ConfigProvider, Empty, Table, Tooltip, message } from 'antd';
import _ from 'lodash';
import React, { useCallback, useState } from 'react';
import React, { useEffect, useState } from 'react';
import {
WORKERS_API,
deleteWorker,
@@ -27,7 +28,6 @@ import {
} from '../apis';
import { WorkerStatusMapValue, status } from '../config';
import { Filesystem, GPUDeviceItem, ListItem } from '../config/types';
import AddWorker from './add-worker';
import UpdateLabels from './update-labels';
const { Column } = Table;
@@ -115,6 +115,7 @@ const Workers: React.FC = () => {
handlePageChange,
handleTableChange,
handleSearch,
handleQueryChange,
handleNameChange
} = useTableFetch<ListItem>({
fetchAPI: queryWorkersList,
@@ -125,7 +126,6 @@ const Workers: React.FC = () => {
});
const intl = useIntl();
const [open, setOpen] = useState(false);
const [updateLabelsData, setUpdateLabelsData] = useState<{
open: boolean;
data: ListItem;
@@ -133,35 +133,65 @@ const Workers: React.FC = () => {
open: false,
data: {} as ListItem
});
const [clusterData, setClusterData] = useState<{
list: Global.BaseOption<number>[];
data: Record<number, string>;
}>({
list: [],
data: {}
});
const handleAddWorker = () => {
setOpen(true);
const getClusterList = async () => {
try {
const params = {
page: 1,
perPage: 100
};
const res = await queryClusterList(params);
const clusterMap = res?.items?.reduce(
(acc: Record<number, string>, item: any) => {
acc[item.id] = item.name;
return acc;
},
{}
);
const list = res?.items?.map((item: any) => ({
label: item.name,
value: item.id
}));
setClusterData({
list,
data: clusterMap
});
} catch (error) {
setClusterData({
list: [],
data: {}
});
}
};
const handleUpdateLabelsOk = useCallback(
async (values: Record<string, any>) => {
try {
console.log('updateLabelsData.data', updateLabelsData.data);
await updateWorker(updateLabelsData.data.id, {
...updateLabelsData.data,
labels: values.labels
});
message.success(intl.formatMessage({ id: 'common.message.success' }));
fetchData();
setUpdateLabelsData({ open: false, data: {} as ListItem });
} catch (error) {
console.log('error', error);
}
},
[updateLabelsData, fetchData]
);
const handleUpdateLabelsOk = async (values: Record<string, any>) => {
try {
console.log('updateLabelsData.data', updateLabelsData.data);
await updateWorker(updateLabelsData.data.id, {
...updateLabelsData.data,
labels: values.labels
});
message.success(intl.formatMessage({ id: 'common.message.success' }));
fetchData();
setUpdateLabelsData({ open: false, data: {} as ListItem });
} catch (error) {
console.log('error', error);
}
};
const handleCancelUpdateLabels = useCallback(() => {
const handleCancelUpdateLabels = () => {
setUpdateLabelsData({
...updateLabelsData,
open: false
});
}, []);
};
const handleUpdateLabels = (record: ListItem) => {
console.log('record', record);
@@ -210,6 +240,17 @@ const Workers: React.FC = () => {
);
};
const handleClusterChange = (value: number) => {
handleQueryChange({
page: 1,
cluster_id: value
});
};
useEffect(() => {
getClusterList();
}, []);
return (
<>
<PageContainer
@@ -232,9 +273,10 @@ const Workers: React.FC = () => {
buttonText={intl.formatMessage({ id: 'resources.button.create' })}
handleDeleteByBatch={handleDeleteBatch}
handleSearch={handleSearch}
handleClickPrimary={handleAddWorker}
handleSelectChange={handleClusterChange}
handleInputChange={handleNameChange}
rowSelection={rowSelection}
selectOptions={clusterData.list}
width={{ input: 200 }}
></FilterBar>
<ConfigProvider renderEmpty={renderEmpty}>
@@ -309,7 +351,7 @@ const Workers: React.FC = () => {
render={(text, record: ListItem) => {
return (
<AutoTooltip ghost maxWidth={240}>
<span>digital-ocean-cluster</span>
<span>{clusterData.data[record.cluster_id]}</span>
</AutoTooltip>
);
}}
@@ -496,7 +538,6 @@ const Workers: React.FC = () => {
</Table>
</ConfigProvider>
<DeleteModal ref={modalRef}></DeleteModal>
<AddWorker open={open} onCancel={() => setOpen(false)}></AddWorker>
<UpdateLabels
open={updateLabelsData.open}
onOk={handleUpdateLabelsOk}
+1
View File
@@ -64,6 +64,7 @@ export interface ListItem {
labels: Record<string, string>;
state: string;
ip: string;
cluster_id: number;
state_message: string;
status: {
cpu: {