fix: gpu selector by cluster
This commit is contained in:
+3
-3
@@ -161,9 +161,9 @@ export default [
|
|||||||
name: 'clusters',
|
name: 'clusters',
|
||||||
path: '/cluster-management/clusters',
|
path: '/cluster-management/clusters',
|
||||||
key: 'clusters',
|
key: 'clusters',
|
||||||
icon: 'icon-k8s-outline',
|
icon: 'icon-cluster2-outline',
|
||||||
selectedIcon: 'icon-k8s-filled',
|
selectedIcon: 'icon-cluster2-filled',
|
||||||
defaultIcon: 'icon-k8s-outline',
|
defaultIcon: 'icon-cluster2-outline',
|
||||||
component: './cluster-management/clusters'
|
component: './cluster-management/clusters'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import {
|
|||||||
EditOutlined,
|
EditOutlined,
|
||||||
ExperimentOutlined,
|
ExperimentOutlined,
|
||||||
FileTextOutlined,
|
FileTextOutlined,
|
||||||
|
KubernetesOutlined,
|
||||||
|
ProfileOutlined,
|
||||||
RetweetOutlined,
|
RetweetOutlined,
|
||||||
ThunderboltOutlined
|
ThunderboltOutlined
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
@@ -20,6 +22,8 @@ const icons = {
|
|||||||
DownloadOutlined: React.createElement(DownloadOutlined),
|
DownloadOutlined: React.createElement(DownloadOutlined),
|
||||||
FileTextOutlined: React.createElement(FileTextOutlined),
|
FileTextOutlined: React.createElement(FileTextOutlined),
|
||||||
ApiOutlined: React.createElement(ApiOutlined),
|
ApiOutlined: React.createElement(ApiOutlined),
|
||||||
|
KubernetesOutlined: React.createElement(KubernetesOutlined),
|
||||||
|
ProfileOutlined: React.createElement(ProfileOutlined),
|
||||||
Stop: React.createElement(IconFont, { type: 'icon-stop1' }),
|
Stop: React.createElement(IconFont, { type: 'icon-stop1' }),
|
||||||
Play: React.createElement(IconFont, { type: 'icon-outline-play' }),
|
Play: React.createElement(IconFont, { type: 'icon-outline-play' }),
|
||||||
Catalog: React.createElement(IconFont, { type: 'icon-catalog' }),
|
Catalog: React.createElement(IconFont, { type: 'icon-catalog' }),
|
||||||
@@ -29,7 +33,9 @@ const icons = {
|
|||||||
LocalPath: React.createElement(IconFont, { type: 'icon-hard-disk' }),
|
LocalPath: React.createElement(IconFont, { type: 'icon-hard-disk' }),
|
||||||
Launch: React.createElement(IconFont, { type: 'icon-rocket-launch' }),
|
Launch: React.createElement(IconFont, { type: 'icon-rocket-launch' }),
|
||||||
Deployment: React.createElement(IconFont, { type: 'icon-rocket-launch1' }),
|
Deployment: React.createElement(IconFont, { type: 'icon-rocket-launch1' }),
|
||||||
Docker: React.createElement(IconFont, { type: 'icon-docker' })
|
Docker: React.createElement(IconFont, { type: 'icon-docker' }),
|
||||||
|
DigitalOcean: React.createElement(IconFont, { type: 'icon-digitalocean' }),
|
||||||
|
DetailInfo: React.createElement(IconFont, { type: 'icon-detail-info' })
|
||||||
};
|
};
|
||||||
|
|
||||||
export default icons;
|
export default icons;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { createFromIconfontCN } from '@ant-design/icons';
|
|||||||
// import './iconfont/iconfont.js';
|
// import './iconfont/iconfont.js';
|
||||||
|
|
||||||
const IconFont = createFromIconfontCN({
|
const IconFont = createFromIconfontCN({
|
||||||
scriptUrl: '//at.alicdn.com/t/c/font_4613488_arejw0wk0kd.js'
|
scriptUrl: '//at.alicdn.com/t/c/font_4613488_wy0l8ooa03c.js'
|
||||||
});
|
});
|
||||||
|
|
||||||
export default IconFont;
|
export default IconFont;
|
||||||
|
|||||||
@@ -56,10 +56,12 @@ const ScrollerModal = (props: ModalProps & { maxContentHeight?: number }) => {
|
|||||||
body: {
|
body: {
|
||||||
padding: '0'
|
padding: '0'
|
||||||
},
|
},
|
||||||
footer: {
|
footer: props.footer
|
||||||
padding: '12px 24px 24px',
|
? {
|
||||||
margin: '0'
|
padding: '12px 24px 24px',
|
||||||
}
|
margin: '0'
|
||||||
|
}
|
||||||
|
: {}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Wrapper
|
<Wrapper
|
||||||
|
|||||||
@@ -46,7 +46,10 @@ const SealSelect: React.FC<SelectProps & SealFormItemProps> = (props) => {
|
|||||||
}, [options, intl]);
|
}, [options, intl]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isNotEmptyValue(props.value) || (allowNull && props.value === null)) {
|
if (
|
||||||
|
isNotEmptyValue(props.value) ||
|
||||||
|
(allowNull && (props.value === null || props.value === undefined))
|
||||||
|
) {
|
||||||
setIsFocus(true);
|
setIsFocus(true);
|
||||||
}
|
}
|
||||||
}, [props.value, allowNull]);
|
}, [props.value, allowNull]);
|
||||||
|
|||||||
Vendored
+4
-4
@@ -24,18 +24,18 @@ declare namespace Global {
|
|||||||
avatar_url?: string;
|
avatar_url?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BaseListItem<T> {
|
type BaseListItem<T, U extends Record<string, any>> = {
|
||||||
key: string;
|
key: string;
|
||||||
locale?: boolean;
|
locale?: boolean;
|
||||||
value: T;
|
value: T;
|
||||||
}
|
} & U;
|
||||||
|
|
||||||
interface BaseOption<T> {
|
type BaseOption<T, U extends Record<string, any>> = {
|
||||||
label: string;
|
label: string;
|
||||||
locale?: boolean;
|
locale?: boolean;
|
||||||
value: T;
|
value: T;
|
||||||
meta?: Record<string, any>;
|
meta?: Record<string, any>;
|
||||||
}
|
} & U;
|
||||||
|
|
||||||
interface HintOptions {
|
interface HintOptions {
|
||||||
label: string;
|
label: string;
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ import {
|
|||||||
NodePoolListItem
|
NodePoolListItem
|
||||||
} from '../config/types';
|
} from '../config/types';
|
||||||
|
|
||||||
export const CREDENTIALS_API = '/credentials';
|
export const CREDENTIALS_API = '/cloud-credentials';
|
||||||
|
|
||||||
export const CLUSTERS_API = '/clusters';
|
export const CLUSTERS_API = '/clusters';
|
||||||
|
|
||||||
export const WORKER_POOLS_API = '/worker-pools';
|
export const WORKER_POOLS_API = '/worker-pools';
|
||||||
|
|
||||||
export const CLUSTER_TOKEN = 'registration_token';
|
export const CLUSTER_TOKEN = 'registration-token';
|
||||||
|
|
||||||
// ===================== Credentials =====================
|
// ===================== Credentials =====================
|
||||||
|
|
||||||
@@ -89,8 +89,8 @@ export async function queryClusterDetail(id: number) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function queryClusterToken(id: number) {
|
export async function queryClusterToken(params: { id: number }) {
|
||||||
return request(`${CLUSTERS_API}/${id}/${CLUSTER_TOKEN}`, {
|
return request(`${CLUSTERS_API}/${params.id}/${CLUSTER_TOKEN}`, {
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import DeleteModal from '@/components/delete-modal';
|
import DeleteModal from '@/components/delete-modal';
|
||||||
import IconFont from '@/components/icon-font';
|
|
||||||
import { FilterBar } from '@/components/page-tools';
|
import { FilterBar } from '@/components/page-tools';
|
||||||
import CardList from '@/components/templates/card-list';
|
import CardList from '@/components/templates/card-list';
|
||||||
import CardSkeleton from '@/components/templates/card-skelton';
|
import CardSkeleton from '@/components/templates/card-skelton';
|
||||||
@@ -7,12 +6,6 @@ import { PageAction } from '@/config';
|
|||||||
import type { PageActionType } from '@/config/types';
|
import type { PageActionType } from '@/config/types';
|
||||||
import useTableFetch from '@/hooks/use-table-fetch';
|
import useTableFetch from '@/hooks/use-table-fetch';
|
||||||
import AddWorker from '@/pages/resources/components/add-worker';
|
import AddWorker from '@/pages/resources/components/add-worker';
|
||||||
import {
|
|
||||||
DeleteOutlined,
|
|
||||||
EditOutlined,
|
|
||||||
KubernetesOutlined,
|
|
||||||
ProfileOutlined
|
|
||||||
} from '@ant-design/icons';
|
|
||||||
import { PageContainer } from '@ant-design/pro-components';
|
import { PageContainer } from '@ant-design/pro-components';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Table, message } from 'antd';
|
import { Table, message } from 'antd';
|
||||||
@@ -21,76 +14,19 @@ import {
|
|||||||
createCluster,
|
createCluster,
|
||||||
deleteCluster,
|
deleteCluster,
|
||||||
queryClusterList,
|
queryClusterList,
|
||||||
|
queryClusterToken,
|
||||||
updateCluster
|
updateCluster
|
||||||
} from './apis';
|
} from './apis';
|
||||||
import AddCluster from './components/add-cluster';
|
import AddCluster from './components/add-cluster';
|
||||||
import ClusterDetailModal from './components/cluster-detail-modal';
|
import ClusterDetailModal from './components/cluster-detail-modal';
|
||||||
import ClusterItem from './components/cluster-item';
|
import ClusterItem from './components/cluster-item';
|
||||||
import { ClusterDataList } from './config';
|
import { ProviderLabelMap, ProviderValueMap, addActions } from './config';
|
||||||
import {
|
import {
|
||||||
ClusterFormData as FormData,
|
ClusterFormData as FormData,
|
||||||
ClusterListItem as ListItem
|
ClusterListItem as ListItem
|
||||||
} from './config/types';
|
} from './config/types';
|
||||||
const { Column } = Table;
|
const { Column } = Table;
|
||||||
|
|
||||||
const addActions = [
|
|
||||||
{
|
|
||||||
label: 'Custom',
|
|
||||||
locale: false,
|
|
||||||
value: 'custom',
|
|
||||||
key: 'custom',
|
|
||||||
icon: <IconFont type="icon-docker" className="size-16" />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Kubernetes',
|
|
||||||
locale: false,
|
|
||||||
value: 'kubernetes',
|
|
||||||
key: 'kubernetes',
|
|
||||||
icon: <KubernetesOutlined className="size-16" />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Digital Ocean',
|
|
||||||
locale: false,
|
|
||||||
value: 'digitalocean',
|
|
||||||
key: 'digitalocean',
|
|
||||||
icon: <IconFont type="icon-digitalocean" />
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const ActionList = [
|
|
||||||
{
|
|
||||||
key: 'edit',
|
|
||||||
label: 'common.button.edit',
|
|
||||||
icon: <EditOutlined></EditOutlined>
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'add',
|
|
||||||
label: 'Add Worker',
|
|
||||||
locale: false,
|
|
||||||
icon: <EditOutlined></EditOutlined>
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
key: 'terminal',
|
|
||||||
label: 'common.button.detail',
|
|
||||||
icon: <ProfileOutlined />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'addPool',
|
|
||||||
label: 'Add Node Pool',
|
|
||||||
locale: false,
|
|
||||||
icon: <IconFont type="icon-catalog1" />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'delete',
|
|
||||||
props: {
|
|
||||||
danger: true
|
|
||||||
},
|
|
||||||
label: 'common.button.delete',
|
|
||||||
icon: <DeleteOutlined></DeleteOutlined>
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const Credentials: React.FC = () => {
|
const Credentials: React.FC = () => {
|
||||||
const {
|
const {
|
||||||
dataSource,
|
dataSource,
|
||||||
@@ -121,15 +57,33 @@ const Credentials: React.FC = () => {
|
|||||||
});
|
});
|
||||||
const [openAddWorker, setOpenAddWorker] = useState<{
|
const [openAddWorker, setOpenAddWorker] = useState<{
|
||||||
open: boolean;
|
open: boolean;
|
||||||
token: string;
|
registrationInfo: {
|
||||||
|
token: string;
|
||||||
|
image: string;
|
||||||
|
server_url: string;
|
||||||
|
};
|
||||||
}>({
|
}>({
|
||||||
open: false,
|
open: false,
|
||||||
token: ''
|
registrationInfo: {
|
||||||
|
token: '',
|
||||||
|
image: '',
|
||||||
|
server_url: ''
|
||||||
|
}
|
||||||
});
|
});
|
||||||
const [openAddModal, setOpenAddModal] = useState(false);
|
const [openAddModal, setOpenAddModal] = useState<{
|
||||||
const [provider, setProvider] = useState<string>('custom');
|
open: boolean;
|
||||||
const [action, setAction] = useState<PageActionType>(PageAction.CREATE);
|
action: PageActionType;
|
||||||
const [title, setTitle] = useState<string>('');
|
currentData?: ListItem;
|
||||||
|
title: string;
|
||||||
|
provider: string;
|
||||||
|
}>({
|
||||||
|
open: false,
|
||||||
|
action: PageAction.CREATE,
|
||||||
|
currentData: undefined,
|
||||||
|
title: '',
|
||||||
|
provider: ''
|
||||||
|
});
|
||||||
|
|
||||||
const [addPoolStatus, setAddPoolStatus] = useState<{
|
const [addPoolStatus, setAddPoolStatus] = useState<{
|
||||||
open: boolean;
|
open: boolean;
|
||||||
action: PageActionType;
|
action: PageActionType;
|
||||||
@@ -139,25 +93,19 @@ const Credentials: React.FC = () => {
|
|||||||
open: false,
|
open: false,
|
||||||
action: PageAction.CREATE,
|
action: PageAction.CREATE,
|
||||||
title: '',
|
title: '',
|
||||||
provider: 'digitalocean'
|
provider: ProviderValueMap.DigitalOcean
|
||||||
});
|
});
|
||||||
const [currentData, setCurrentData] = useState<ListItem | undefined>(
|
|
||||||
undefined
|
|
||||||
);
|
|
||||||
|
|
||||||
const setActions = (row: ListItem) => {
|
|
||||||
if (row.provider !== 'custom') {
|
|
||||||
return ActionList.filter((item) => item.key !== 'add');
|
|
||||||
}
|
|
||||||
return ActionList;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleAddCluster = (value: string) => {
|
const handleAddCluster = (value: string) => {
|
||||||
setOpenAddModal(true);
|
const label = ProviderLabelMap[value];
|
||||||
setAction(PageAction.CREATE);
|
|
||||||
setProvider(value);
|
setOpenAddModal({
|
||||||
const label = addActions.find((item) => item.value === value)?.label;
|
open: true,
|
||||||
setTitle(`Add ${label} Cluster`);
|
action: PageAction.CREATE,
|
||||||
|
currentData: undefined,
|
||||||
|
title: `Add ${label} Cluster`,
|
||||||
|
provider: value
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddPool = (value: string) => {
|
const handleAddPool = (value: string) => {
|
||||||
@@ -178,32 +126,65 @@ const Credentials: React.FC = () => {
|
|||||||
...data
|
...data
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
if (action === PageAction.EDIT) {
|
if (openAddModal.action === PageAction.EDIT) {
|
||||||
await updateCluster({
|
await updateCluster({
|
||||||
data: params,
|
data: params,
|
||||||
id: currentData!.id
|
id: openAddModal.currentData!.id
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await createCluster({ data: params });
|
await createCluster({ data: params });
|
||||||
}
|
}
|
||||||
fetchData();
|
fetchData();
|
||||||
setOpenAddModal(false);
|
setOpenAddModal({
|
||||||
|
open: false,
|
||||||
|
action: PageAction.CREATE,
|
||||||
|
currentData: undefined,
|
||||||
|
title: '',
|
||||||
|
provider: ''
|
||||||
|
});
|
||||||
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setOpenAddModal(false);
|
setOpenAddModal({
|
||||||
|
open: false,
|
||||||
|
action: PageAction.CREATE,
|
||||||
|
currentData: undefined,
|
||||||
|
title: '',
|
||||||
|
provider: ''
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleModalCancel = () => {
|
const handleModalCancel = () => {
|
||||||
console.log('handleModalCancel');
|
console.log('handleModalCancel');
|
||||||
setOpenAddModal(false);
|
setOpenAddModal({
|
||||||
|
open: false,
|
||||||
|
action: PageAction.CREATE,
|
||||||
|
currentData: undefined,
|
||||||
|
title: '',
|
||||||
|
provider: ''
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEditCluster = (row: ListItem) => {
|
const handleEditCluster = (row: ListItem) => {
|
||||||
setCurrentData(row);
|
setOpenAddModal({
|
||||||
setOpenAddModal(true);
|
open: true,
|
||||||
setAction(PageAction.EDIT);
|
action: PageAction.EDIT,
|
||||||
setTitle(`Edit ${row.name} Cluster`);
|
currentData: row,
|
||||||
|
title: `Edit ${row.name} Cluster`,
|
||||||
|
provider: row.provider
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddWorker = async (row: ListItem) => {
|
||||||
|
try {
|
||||||
|
const data = await queryClusterToken({ id: row.id });
|
||||||
|
setOpenAddWorker({
|
||||||
|
open: true,
|
||||||
|
registrationInfo: data
|
||||||
|
});
|
||||||
|
} catch (error: any) {
|
||||||
|
message.error(error.message || 'Failed to fetch cluster token');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSelect = (val: any, row: ListItem) => {
|
const handleSelect = (val: any, row: ListItem) => {
|
||||||
@@ -212,11 +193,7 @@ const Credentials: React.FC = () => {
|
|||||||
} else if (val === 'delete') {
|
} else if (val === 'delete') {
|
||||||
handleDelete({ ...row, name: row.name });
|
handleDelete({ ...row, name: row.name });
|
||||||
} else if (val === 'add_worker') {
|
} else if (val === 'add_worker') {
|
||||||
setOpenAddWorker({
|
handleAddWorker(row);
|
||||||
open: true,
|
|
||||||
token: '${token}'
|
|
||||||
});
|
|
||||||
setCurrentData(row);
|
|
||||||
} else if (val === 'addPool') {
|
} else if (val === 'addPool') {
|
||||||
handleAddPool(row.provider);
|
handleAddPool(row.provider);
|
||||||
} else if (val === 'details') {
|
} else if (val === 'details') {
|
||||||
@@ -255,14 +232,14 @@ const Credentials: React.FC = () => {
|
|||||||
marginTop={30}
|
marginTop={30}
|
||||||
handleInputChange={handleNameChange}
|
handleInputChange={handleNameChange}
|
||||||
handleSearch={handleSearch}
|
handleSearch={handleSearch}
|
||||||
width={{ input: 200 }}
|
width={{ input: 300 }}
|
||||||
buttonText="Add Cluster"
|
buttonText="Add Cluster"
|
||||||
actionType="dropdown"
|
actionType="dropdown"
|
||||||
actionItems={addActions}
|
actionItems={addActions}
|
||||||
handleClickPrimary={handleClickDropdown}
|
handleClickPrimary={handleClickDropdown}
|
||||||
></FilterBar>
|
></FilterBar>
|
||||||
<CardList
|
<CardList
|
||||||
dataList={ClusterDataList}
|
dataList={dataSource.dataList}
|
||||||
loading={dataSource.loading}
|
loading={dataSource.loading}
|
||||||
activeId={-1}
|
activeId={-1}
|
||||||
isFirst={!dataSource.loadend}
|
isFirst={!dataSource.loadend}
|
||||||
@@ -273,18 +250,23 @@ const Credentials: React.FC = () => {
|
|||||||
></CardList>
|
></CardList>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
<AddCluster
|
<AddCluster
|
||||||
provider={provider}
|
provider={openAddModal.provider}
|
||||||
open={openAddModal}
|
open={openAddModal.open}
|
||||||
action={action}
|
action={openAddModal.action}
|
||||||
title={title}
|
title={openAddModal.title}
|
||||||
data={currentData}
|
currentData={openAddModal.currentData}
|
||||||
onCancel={handleModalCancel}
|
onCancel={handleModalCancel}
|
||||||
onOk={handleModalOk}
|
onOk={handleModalOk}
|
||||||
></AddCluster>
|
></AddCluster>
|
||||||
<AddWorker
|
<AddWorker
|
||||||
open={openAddWorker.open}
|
open={openAddWorker.open}
|
||||||
onCancel={() => setOpenAddWorker({ open: false, token: '' })}
|
onCancel={() =>
|
||||||
token={openAddWorker.token}
|
setOpenAddWorker({
|
||||||
|
open: false,
|
||||||
|
registrationInfo: { token: '', image: '', server_url: '' }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
registrationInfo={openAddWorker.registrationInfo}
|
||||||
></AddWorker>
|
></AddWorker>
|
||||||
<ClusterDetailModal
|
<ClusterDetailModal
|
||||||
open={openClusterDetail.open}
|
open={openClusterDetail.open}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ type AddModalProps = {
|
|||||||
title: string;
|
title: string;
|
||||||
action: PageActionType;
|
action: PageActionType;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
|
currentData?: ListItem; // Used when action is EDIT
|
||||||
provider: string; // 'kubernetes' | 'custom' | 'digitalocean';
|
provider: string; // 'kubernetes' | 'custom' | 'digitalocean';
|
||||||
onOk: (values: FormData) => void;
|
onOk: (values: FormData) => void;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
@@ -28,6 +29,7 @@ const AddCluster: React.FC<AddModalProps> = ({
|
|||||||
action,
|
action,
|
||||||
open,
|
open,
|
||||||
provider,
|
provider,
|
||||||
|
currentData,
|
||||||
onOk,
|
onOk,
|
||||||
onCancel
|
onCancel
|
||||||
}) => {
|
}) => {
|
||||||
@@ -36,12 +38,20 @@ const AddCluster: React.FC<AddModalProps> = ({
|
|||||||
const [submissionStatus, setSubmissionStatus] = React.useState<{
|
const [submissionStatus, setSubmissionStatus] = React.useState<{
|
||||||
success: boolean;
|
success: boolean;
|
||||||
data: ListItem;
|
data: ListItem;
|
||||||
}>({ success: true, data: {} as ListItem });
|
}>({ success: false, data: {} as ListItem });
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
form.submit();
|
form.submit();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleOk = async (data: FormData) => {
|
||||||
|
console.log('handleOk===', data);
|
||||||
|
onOk({
|
||||||
|
...data,
|
||||||
|
provider
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
onCancel();
|
onCancel();
|
||||||
@@ -109,9 +119,14 @@ const AddCluster: React.FC<AddModalProps> = ({
|
|||||||
{submissionStatus.success ? (
|
{submissionStatus.success ? (
|
||||||
renderAddWorkerContent()
|
renderAddWorkerContent()
|
||||||
) : (
|
) : (
|
||||||
<Form form={form} onFinish={onOk} preserve={false}>
|
<Form
|
||||||
|
form={form}
|
||||||
|
onFinish={handleOk}
|
||||||
|
preserve={false}
|
||||||
|
initialValues={currentData}
|
||||||
|
>
|
||||||
<Form.Item<FormData>
|
<Form.Item<FormData>
|
||||||
name="display_name"
|
name="name"
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
@@ -129,7 +144,7 @@ const AddCluster: React.FC<AddModalProps> = ({
|
|||||||
required
|
required
|
||||||
></SealInput.Input>
|
></SealInput.Input>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{provider === 'digitalocean' && (
|
{provider === ProviderValueMap.DigitalOcean && (
|
||||||
<CloudProvider provider={provider}></CloudProvider>
|
<CloudProvider provider={provider}></CloudProvider>
|
||||||
)}
|
)}
|
||||||
<Form.Item<FormData> name="description" rules={[{ required: false }]}>
|
<Form.Item<FormData> name="description" rules={[{ required: false }]}>
|
||||||
|
|||||||
@@ -1,19 +1,23 @@
|
|||||||
import ModalFooter from '@/components/modal-footer';
|
import ModalFooter from '@/components/modal-footer';
|
||||||
import ScrollerModal from '@/components/scroller-modal';
|
import ScrollerModal from '@/components/scroller-modal';
|
||||||
import SealInput from '@/components/seal-form/seal-input';
|
import SealInput from '@/components/seal-form/seal-input';
|
||||||
import { PageAction, PasswordReg } from '@/config';
|
import { PageAction } from '@/config';
|
||||||
import { PageActionType } from '@/config/types';
|
import { PageActionType } from '@/config/types';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Button, Form } from 'antd';
|
import { Button, Form } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FormData, ListItem } from '../config/types';
|
import { ProviderValueMap } from '../config';
|
||||||
|
import {
|
||||||
|
CredentialFormData as FormData,
|
||||||
|
CredentialListItem as ListItem
|
||||||
|
} from '../config/types';
|
||||||
|
|
||||||
type AddModalProps = {
|
type AddModalProps = {
|
||||||
title: string;
|
title: string;
|
||||||
action: PageActionType;
|
action: PageActionType;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onOk: (values: FormData) => void;
|
onOk: (values: FormData) => void;
|
||||||
data?: ListItem;
|
currentData?: ListItem;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
provider: string; // 'kubernetes' | 'digitalocean';
|
provider: string; // 'kubernetes' | 'digitalocean';
|
||||||
};
|
};
|
||||||
@@ -22,7 +26,7 @@ const AddModal: React.FC<AddModalProps> = ({
|
|||||||
action,
|
action,
|
||||||
open,
|
open,
|
||||||
onOk,
|
onOk,
|
||||||
data,
|
currentData,
|
||||||
provider,
|
provider,
|
||||||
onCancel
|
onCancel
|
||||||
}) => {
|
}) => {
|
||||||
@@ -33,6 +37,10 @@ const AddModal: React.FC<AddModalProps> = ({
|
|||||||
form.submit();
|
form.submit();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleOk = async (data: FormData) => {
|
||||||
|
onOk(data);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollerModal
|
<ScrollerModal
|
||||||
title={title}
|
title={title}
|
||||||
@@ -53,7 +61,12 @@ const AddModal: React.FC<AddModalProps> = ({
|
|||||||
></ModalFooter>
|
></ModalFooter>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Form form={form} onFinish={onOk} preserve={false}>
|
<Form
|
||||||
|
form={form}
|
||||||
|
onFinish={handleOk}
|
||||||
|
preserve={false}
|
||||||
|
initialValues={currentData}
|
||||||
|
>
|
||||||
<Form.Item<FormData>
|
<Form.Item<FormData>
|
||||||
name="name"
|
name="name"
|
||||||
rules={[
|
rules={[
|
||||||
@@ -73,14 +86,13 @@ const AddModal: React.FC<AddModalProps> = ({
|
|||||||
required
|
required
|
||||||
></SealInput.Input>
|
></SealInput.Input>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{provider === 'digital_ocean' && (
|
{provider === ProviderValueMap.DigitalOcean && (
|
||||||
<>
|
<>
|
||||||
<Form.Item<FormData>
|
<Form.Item<FormData>
|
||||||
name="access_key"
|
name="key"
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: action === PageAction.CREATE,
|
required: action === PageAction.CREATE,
|
||||||
pattern: PasswordReg,
|
|
||||||
message: intl.formatMessage({
|
message: intl.formatMessage({
|
||||||
id: 'users.form.rule.password'
|
id: 'users.form.rule.password'
|
||||||
})
|
})
|
||||||
@@ -93,11 +105,10 @@ const AddModal: React.FC<AddModalProps> = ({
|
|||||||
></SealInput.Password>
|
></SealInput.Password>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item<FormData>
|
<Form.Item<FormData>
|
||||||
name="secret_key"
|
name="secret"
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: action === PageAction.CREATE,
|
required: action === PageAction.CREATE,
|
||||||
pattern: PasswordReg,
|
|
||||||
message: intl.formatMessage({
|
message: intl.formatMessage({
|
||||||
id: 'users.form.rule.password'
|
id: 'users.form.rule.password'
|
||||||
})
|
})
|
||||||
@@ -105,17 +116,12 @@ const AddModal: React.FC<AddModalProps> = ({
|
|||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<SealInput.Password
|
<SealInput.Password
|
||||||
label="Secret Key"
|
label="Access Secret"
|
||||||
required={action === PageAction.CREATE}
|
required={action === PageAction.CREATE}
|
||||||
></SealInput.Password>
|
></SealInput.Password>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{provider === 'kubernetes' && (
|
|
||||||
<Form.Item<FormData> name="kubeconfig" rules={[{ required: false }]}>
|
|
||||||
<SealInput.TextArea label="Kubeconfig"></SealInput.TextArea>
|
|
||||||
</Form.Item>
|
|
||||||
)}
|
|
||||||
<Form.Item<FormData> name="description" rules={[{ required: false }]}>
|
<Form.Item<FormData> name="description" rules={[{ required: false }]}>
|
||||||
<SealInput.TextArea
|
<SealInput.TextArea
|
||||||
label={intl.formatMessage({ id: 'common.table.description' })}
|
label={intl.formatMessage({ id: 'common.table.description' })}
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ const CloudProvider: React.FC<CloudProviderProps> = () => {
|
|||||||
label="Credential"
|
label="Credential"
|
||||||
required
|
required
|
||||||
options={['credential1', 'credential2', 'credential3'].map(
|
options={['credential1', 'credential2', 'credential3'].map(
|
||||||
(item) => ({
|
(item, i) => ({
|
||||||
label: item,
|
label: item,
|
||||||
value: item
|
value: i
|
||||||
})
|
})
|
||||||
)}
|
)}
|
||||||
></SealSelect>
|
></SealSelect>
|
||||||
|
|||||||
@@ -5,78 +5,22 @@ import StatusTag from '@/components/status-tag';
|
|||||||
import ThemeTag from '@/components/tags-wrapper/theme-tag';
|
import ThemeTag from '@/components/tags-wrapper/theme-tag';
|
||||||
import Card from '@/components/templates/card';
|
import Card from '@/components/templates/card';
|
||||||
import { PageAction } from '@/config';
|
import { PageAction } from '@/config';
|
||||||
import {
|
|
||||||
DeleteOutlined,
|
|
||||||
EditOutlined,
|
|
||||||
KubernetesOutlined
|
|
||||||
} from '@ant-design/icons';
|
|
||||||
import { Card as ACard, Col, Collapse, Row } from 'antd';
|
import { Card as ACard, Col, Collapse, Row } from 'antd';
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import { queryClusterToken } from '../apis';
|
||||||
import {
|
import {
|
||||||
ClusterStatus,
|
ClusterStatus,
|
||||||
ClusterStatusLabelMap,
|
ClusterStatusLabelMap,
|
||||||
ProviderLabelMap,
|
ProviderLabelMap,
|
||||||
ProviderValueMap
|
ProviderValueMap,
|
||||||
|
poolActionList
|
||||||
} from '../config';
|
} from '../config';
|
||||||
import { ClusterListItem as ListItem, NodePoolListItem } from '../config/types';
|
import { ClusterListItem as ListItem, NodePoolListItem } from '../config/types';
|
||||||
import AddPool from './add-pool';
|
import AddPool from './add-pool';
|
||||||
import RegisterCluster from './register-cluster';
|
import RegisterCluster from './register-cluster';
|
||||||
import WorkerPools from './worker-pools';
|
import WorkerPools from './worker-pools';
|
||||||
|
|
||||||
const actionItems = [
|
|
||||||
{
|
|
||||||
key: 'edit',
|
|
||||||
label: 'common.button.edit',
|
|
||||||
icon: <EditOutlined />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'details',
|
|
||||||
label: 'common.button.view',
|
|
||||||
icon: <IconFont type="icon-detail-info" className="font-size-16" />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'add_worker',
|
|
||||||
label: 'Add Worker',
|
|
||||||
provider: ProviderValueMap.Custom,
|
|
||||||
locale: false,
|
|
||||||
icon: <IconFont type="icon-docker" />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'register_cluster',
|
|
||||||
label: 'Register Cluster',
|
|
||||||
provider: ProviderValueMap.Kubernetes,
|
|
||||||
locale: false,
|
|
||||||
icon: <KubernetesOutlined />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'addPool',
|
|
||||||
label: 'Add Node Pool',
|
|
||||||
provider: ProviderValueMap.DigitalOcean,
|
|
||||||
locale: false,
|
|
||||||
icon: <IconFont type="icon-catalog1" />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'delete',
|
|
||||||
label: 'common.button.delete',
|
|
||||||
icon: <DeleteOutlined />,
|
|
||||||
props: {
|
|
||||||
danger: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const CollapseTitle = styled.div`
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
font-size: var(--font-size-middle);
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--ant-color-text);
|
|
||||||
height: 32px;
|
|
||||||
cursor: pointer;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Content = styled.div`
|
const Content = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@@ -216,12 +160,39 @@ const CardItem: React.FC<CardProps> = (props) => {
|
|||||||
open: false,
|
open: false,
|
||||||
action: PageAction.CREATE,
|
action: PageAction.CREATE,
|
||||||
title: '',
|
title: '',
|
||||||
provider: 'digitalocean'
|
provider: ProviderValueMap.DigitalOcean
|
||||||
});
|
});
|
||||||
const [registerClusterStatus, setRegisterClusterStatus] = React.useState({
|
const [registerClusterStatus, setRegisterClusterStatus] = React.useState<{
|
||||||
open: false
|
open: boolean;
|
||||||
|
registrationInfo: {
|
||||||
|
token: string;
|
||||||
|
image: string;
|
||||||
|
server_url: string;
|
||||||
|
cluster_id: number;
|
||||||
|
};
|
||||||
|
}>({
|
||||||
|
open: false,
|
||||||
|
registrationInfo: {
|
||||||
|
token: '',
|
||||||
|
image: '',
|
||||||
|
server_url: '',
|
||||||
|
cluster_id: 0
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleRegisterCluster = async () => {
|
||||||
|
try {
|
||||||
|
const info = await queryClusterToken({ id: data.id });
|
||||||
|
setRegisterClusterStatus({
|
||||||
|
open: true,
|
||||||
|
registrationInfo: {
|
||||||
|
...info,
|
||||||
|
cluster_id: data.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {}
|
||||||
|
};
|
||||||
|
|
||||||
// cluster action handler
|
// cluster action handler
|
||||||
const handleOnSelect = (key: string) => {
|
const handleOnSelect = (key: string) => {
|
||||||
if (key === 'addPool') {
|
if (key === 'addPool') {
|
||||||
@@ -235,9 +206,7 @@ const CardItem: React.FC<CardProps> = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (key === 'register_cluster') {
|
if (key === 'register_cluster') {
|
||||||
setRegisterClusterStatus({
|
handleRegisterCluster();
|
||||||
open: true
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onSelect?.(key, data);
|
onSelect?.(key, data);
|
||||||
@@ -256,7 +225,7 @@ const CardItem: React.FC<CardProps> = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const actions = useMemo(() => {
|
const actions = useMemo(() => {
|
||||||
return actionItems.filter((item) => {
|
return poolActionList.filter((item) => {
|
||||||
if (item.provider) {
|
if (item.provider) {
|
||||||
return item.provider === data.provider;
|
return item.provider === data.provider;
|
||||||
}
|
}
|
||||||
@@ -269,7 +238,7 @@ const CardItem: React.FC<CardProps> = (props) => {
|
|||||||
height={'auto'}
|
height={'auto'}
|
||||||
clickable={false}
|
clickable={false}
|
||||||
ghost
|
ghost
|
||||||
footer={
|
footerHolder={
|
||||||
<CollapseWrapper
|
<CollapseWrapper
|
||||||
onChange={() => setShow(!show)}
|
onChange={() => setShow(!show)}
|
||||||
expandIconPosition="end"
|
expandIconPosition="end"
|
||||||
@@ -340,11 +309,13 @@ const CardItem: React.FC<CardProps> = (props) => {
|
|||||||
<div className="title">
|
<div className="title">
|
||||||
<span className="flex-center gap-8">
|
<span className="flex-center gap-8">
|
||||||
<span className="text">{data.name}</span>
|
<span className="text">{data.name}</span>
|
||||||
<ThemeTag>{ProviderLabelMap[data.provider]}</ThemeTag>
|
<ThemeTag color="purple">
|
||||||
|
{ProviderLabelMap[data.provider]}
|
||||||
|
</ThemeTag>
|
||||||
<StatusTag
|
<StatusTag
|
||||||
statusValue={{
|
statusValue={{
|
||||||
status: ClusterStatus[data.status],
|
status: ClusterStatus[data.state],
|
||||||
text: ClusterStatusLabelMap[data.status]
|
text: ClusterStatusLabelMap[data.state]
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
@@ -359,15 +330,17 @@ const CardItem: React.FC<CardProps> = (props) => {
|
|||||||
<CardBox>
|
<CardBox>
|
||||||
<CardWrapper bordered={false}>
|
<CardWrapper bordered={false}>
|
||||||
<div className="label">Workers</div>
|
<div className="label">Workers</div>
|
||||||
<div className="value">1/1</div>
|
<div className="value">
|
||||||
|
{data.ready_workers} / {data.workers}
|
||||||
|
</div>
|
||||||
</CardWrapper>
|
</CardWrapper>
|
||||||
<CardWrapper bordered={false}>
|
<CardWrapper bordered={false}>
|
||||||
<div className="label">GPUs</div>
|
<div className="label">GPUs</div>
|
||||||
<div className="value">12</div>
|
<div className="value">{data.gpus}</div>
|
||||||
</CardWrapper>
|
</CardWrapper>
|
||||||
<CardWrapper bordered={false}>
|
<CardWrapper bordered={false}>
|
||||||
<div className="label">Deployments</div>
|
<div className="label">Deployments</div>
|
||||||
<div className="value">2</div>
|
<div className="value">{data.models}</div>
|
||||||
</CardWrapper>
|
</CardWrapper>
|
||||||
</CardBox>
|
</CardBox>
|
||||||
</Content>
|
</Content>
|
||||||
@@ -397,10 +370,16 @@ const CardItem: React.FC<CardProps> = (props) => {
|
|||||||
<RegisterCluster
|
<RegisterCluster
|
||||||
title="Register Cluster"
|
title="Register Cluster"
|
||||||
open={registerClusterStatus.open}
|
open={registerClusterStatus.open}
|
||||||
data={data}
|
registrationInfo={registerClusterStatus.registrationInfo}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
setRegisterClusterStatus({
|
setRegisterClusterStatus({
|
||||||
open: false
|
open: false,
|
||||||
|
registrationInfo: {
|
||||||
|
token: '',
|
||||||
|
image: '',
|
||||||
|
server_url: '',
|
||||||
|
cluster_id: 0
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
></RegisterCluster>
|
></RegisterCluster>
|
||||||
|
|||||||
@@ -1,18 +1,22 @@
|
|||||||
import ScrollerModal from '@/components/scroller-modal/index';
|
import ScrollerModal from '@/components/scroller-modal/index';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ClusterListItem as ListItem } from '../config/types';
|
|
||||||
import RegisterClusterInner from './resiter-cluster-inner';
|
import RegisterClusterInner from './resiter-cluster-inner';
|
||||||
|
|
||||||
type AddModalProps = {
|
type AddModalProps = {
|
||||||
title: string;
|
title: string;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
data: ListItem;
|
registrationInfo: {
|
||||||
|
token: string;
|
||||||
|
image: string;
|
||||||
|
server_url: string;
|
||||||
|
cluster_id: number;
|
||||||
|
};
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
};
|
};
|
||||||
const AddCluster: React.FC<AddModalProps> = ({
|
const AddCluster: React.FC<AddModalProps> = ({
|
||||||
title,
|
title,
|
||||||
open,
|
open,
|
||||||
data,
|
registrationInfo,
|
||||||
onCancel
|
onCancel
|
||||||
}) => {
|
}) => {
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
@@ -31,7 +35,7 @@ const AddCluster: React.FC<AddModalProps> = ({
|
|||||||
width={600}
|
width={600}
|
||||||
footer={false}
|
footer={false}
|
||||||
>
|
>
|
||||||
<RegisterClusterInner data={data} />
|
<RegisterClusterInner registrationInfo={registrationInfo} />
|
||||||
</ScrollerModal>
|
</ScrollerModal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,41 +1,39 @@
|
|||||||
import HighlightCode from '@/components/highlight-code';
|
import HighlightCode from '@/components/highlight-code';
|
||||||
import React, { useEffect } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { queryClusterToken } from '../apis';
|
import styled from 'styled-components';
|
||||||
import { generateRegisterCommand } from '../config';
|
import { generateRegisterCommand } from '../config';
|
||||||
import { ClusterListItem as ListItem } from '../config/types';
|
|
||||||
|
const Title = styled.h3`
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--ant-color-text);
|
||||||
|
margin-bottom: 12px;
|
||||||
|
margin-top: 12px;
|
||||||
|
font-size: var(--font-size-normal);
|
||||||
|
.ant-tag {
|
||||||
|
color: var(--ant-color-text-secondary);
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
type AddModalProps = {
|
type AddModalProps = {
|
||||||
data: ListItem;
|
registrationInfo: {
|
||||||
|
token: string;
|
||||||
|
image: string;
|
||||||
|
server_url: string;
|
||||||
|
cluster_id: number;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
const AddCluster: React.FC<AddModalProps> = ({ data }) => {
|
const AddCluster: React.FC<AddModalProps> = ({ registrationInfo }) => {
|
||||||
const [code, setCode] = React.useState<string>('');
|
const code = useMemo(() => {
|
||||||
const getToken = async () => {
|
return generateRegisterCommand({
|
||||||
const res = await queryClusterToken(data?.id);
|
server: registrationInfo?.server_url || window.location.origin,
|
||||||
return res.data?.token || '';
|
clusterId: registrationInfo?.cluster_id,
|
||||||
};
|
registrationToken: registrationInfo?.token
|
||||||
|
});
|
||||||
|
}, [registrationInfo]);
|
||||||
|
|
||||||
const getCode = async () => {
|
const applyCommand = useMemo(() => {
|
||||||
try {
|
return `kubectl apply -f manifest.yaml`;
|
||||||
const token = await getToken();
|
|
||||||
const command = generateRegisterCommand({
|
|
||||||
server: window.location.origin,
|
|
||||||
clusterId: data?.id || 0,
|
|
||||||
registrationToken: token
|
|
||||||
});
|
|
||||||
setCode(command);
|
|
||||||
} catch (error) {
|
|
||||||
setCode(
|
|
||||||
generateRegisterCommand({
|
|
||||||
server: window.location.origin,
|
|
||||||
clusterId: data?.id || 0,
|
|
||||||
registrationToken: '{token}'
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getCode();
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,57 +1,29 @@
|
|||||||
|
import icons from '@/components/icon-font/icons';
|
||||||
import { StatusMaps } from '@/config';
|
import { StatusMaps } from '@/config';
|
||||||
import { StatusType } from '@/config/types';
|
import { StatusType } from '@/config/types';
|
||||||
|
|
||||||
export const ClusterDataList = [
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
name: 'Custom-cluster',
|
|
||||||
provider: 'custom',
|
|
||||||
clusterType: 'Custom',
|
|
||||||
workers: 4,
|
|
||||||
gpus: 8,
|
|
||||||
status: 'ready',
|
|
||||||
deployments: 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: 'kubernetes-cluster',
|
|
||||||
provider: 'kubernetes',
|
|
||||||
clusterType: 'Kubernetes',
|
|
||||||
workers: 2,
|
|
||||||
gpus: 4,
|
|
||||||
status: 'ready',
|
|
||||||
deployments: 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: 'Digital-Ocean-cluster',
|
|
||||||
provider: 'digitalocean',
|
|
||||||
workers: 3,
|
|
||||||
gpus: 6,
|
|
||||||
status: 'error',
|
|
||||||
deployments: 2
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
export const ClusterStatusValueMap = {
|
export const ClusterStatusValueMap = {
|
||||||
Ready: 'ready',
|
Provisioning: 0,
|
||||||
Error: 'error'
|
Ready: 3,
|
||||||
|
ProvisionedNotReady: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ClusterStatusLabelMap = {
|
export const ClusterStatusLabelMap = {
|
||||||
|
[ClusterStatusValueMap.Provisioning]: 'Provisioning',
|
||||||
[ClusterStatusValueMap.Ready]: 'Ready',
|
[ClusterStatusValueMap.Ready]: 'Ready',
|
||||||
[ClusterStatusValueMap.Error]: 'Error'
|
[ClusterStatusValueMap.ProvisionedNotReady]: 'Provisioned Not Ready'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ClusterStatus: Record<string, StatusType> = {
|
export const ClusterStatus: Record<string, StatusType> = {
|
||||||
|
[ClusterStatusValueMap.Provisioning]: StatusMaps.transitioning,
|
||||||
[ClusterStatusValueMap.Ready]: StatusMaps.success,
|
[ClusterStatusValueMap.Ready]: StatusMaps.success,
|
||||||
[ClusterStatusValueMap.Error]: StatusMaps.error
|
[ClusterStatusValueMap.ProvisionedNotReady]: StatusMaps.error
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ProviderValueMap = {
|
export const ProviderValueMap = {
|
||||||
Kubernetes: 'kubernetes',
|
Kubernetes: 'Kubernetes',
|
||||||
DigitalOcean: 'digitalocean',
|
DigitalOcean: 'DigitalOcean',
|
||||||
Custom: 'custom'
|
Custom: 'Custom'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ProviderLabelMap = {
|
export const ProviderLabelMap = {
|
||||||
@@ -65,6 +37,72 @@ export const generateRegisterCommand = (params: {
|
|||||||
clusterId: number;
|
clusterId: number;
|
||||||
registrationToken: string;
|
registrationToken: string;
|
||||||
}) => {
|
}) => {
|
||||||
return `curl -k -L '${params.server}/v2/clusters/${params.clusterId}/manifests' \\
|
return `curl -k -L '${params.server}/v1/clusters/${params.clusterId}/manifests' \\
|
||||||
--header 'Authorization: Bearer ${params.registrationToken}'`;
|
--header 'Authorization: Bearer ${params.registrationToken}' | kubectl apply -f -`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const addActions = [
|
||||||
|
{
|
||||||
|
label: 'Custom',
|
||||||
|
locale: false,
|
||||||
|
value: ProviderValueMap.Custom,
|
||||||
|
key: ProviderValueMap.Custom,
|
||||||
|
icon: icons.Docker
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Kubernetes',
|
||||||
|
locale: false,
|
||||||
|
value: ProviderValueMap.Kubernetes,
|
||||||
|
key: ProviderValueMap.Kubernetes,
|
||||||
|
icon: icons.KubernetesOutlined
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Digital Ocean',
|
||||||
|
locale: false,
|
||||||
|
value: ProviderValueMap.DigitalOcean,
|
||||||
|
key: ProviderValueMap.DigitalOcean,
|
||||||
|
icon: icons.DigitalOcean
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export const poolActionList = [
|
||||||
|
{
|
||||||
|
key: 'edit',
|
||||||
|
label: 'common.button.edit',
|
||||||
|
icon: icons.EditOutlined
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'details',
|
||||||
|
label: 'common.button.view',
|
||||||
|
icon: icons.DetailInfo
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'add_worker',
|
||||||
|
label: 'Add Worker',
|
||||||
|
provider: ProviderValueMap.Custom,
|
||||||
|
locale: false,
|
||||||
|
icon: icons.Docker
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'register_cluster',
|
||||||
|
label: 'Register Cluster',
|
||||||
|
provider: ProviderValueMap.Kubernetes,
|
||||||
|
locale: false,
|
||||||
|
icon: icons.KubernetesOutlined
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'addPool',
|
||||||
|
label: 'Add Node Pool',
|
||||||
|
provider: ProviderValueMap.DigitalOcean,
|
||||||
|
locale: false,
|
||||||
|
icon: icons.Catalog
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'delete',
|
||||||
|
label: 'common.button.delete',
|
||||||
|
icon: icons.DeleteOutlined,
|
||||||
|
props: {
|
||||||
|
danger: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
export interface CredentialFormData {
|
export interface CredentialFormData {
|
||||||
name: string;
|
name: string;
|
||||||
provider: string;
|
provider: string;
|
||||||
access_key: string;
|
key: string;
|
||||||
secret_key: string;
|
secret: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
id?: number;
|
id?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ClusterStatusType = 0 | 1 | 3;
|
||||||
|
|
||||||
export interface CredentialListItem {
|
export interface CredentialListItem {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -19,7 +21,7 @@ export interface CredentialListItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ClusterFormData {
|
export interface ClusterFormData {
|
||||||
display_name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
provider: string;
|
provider: string;
|
||||||
credential_id: number;
|
credential_id: number;
|
||||||
@@ -55,9 +57,11 @@ export interface ClusterListItem {
|
|||||||
zone: string;
|
zone: string;
|
||||||
region: string;
|
region: string;
|
||||||
gpus: number;
|
gpus: number;
|
||||||
deployments: number;
|
models: number;
|
||||||
|
workers: number;
|
||||||
|
ready_workers: number;
|
||||||
id: number;
|
id: number;
|
||||||
status: string;
|
state: ClusterStatusType;
|
||||||
state_message: string;
|
state_message: string;
|
||||||
worker_pools: NodePoolListItem[];
|
worker_pools: NodePoolListItem[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,19 @@
|
|||||||
import AutoTooltip from '@/components/auto-tooltip';
|
import AutoTooltip from '@/components/auto-tooltip';
|
||||||
import DeleteModal from '@/components/delete-modal';
|
import DeleteModal from '@/components/delete-modal';
|
||||||
import DropDownActions from '@/components/drop-down-actions';
|
|
||||||
import DropdownButtons from '@/components/drop-down-buttons';
|
import DropdownButtons from '@/components/drop-down-buttons';
|
||||||
import IconFont from '@/components/icon-font';
|
import IconFont from '@/components/icon-font';
|
||||||
import PageTools from '@/components/page-tools';
|
import { FilterBar } from '@/components/page-tools';
|
||||||
import { PageAction } from '@/config';
|
import { PageAction } from '@/config';
|
||||||
import type { PageActionType } from '@/config/types';
|
import type { PageActionType } from '@/config/types';
|
||||||
import useTableFetch from '@/hooks/use-table-fetch';
|
import useTableFetch from '@/hooks/use-table-fetch';
|
||||||
import {
|
import {
|
||||||
DeleteOutlined,
|
DeleteOutlined,
|
||||||
DownOutlined,
|
|
||||||
EditOutlined,
|
EditOutlined,
|
||||||
KubernetesOutlined,
|
KubernetesOutlined
|
||||||
SyncOutlined
|
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { PageContainer } from '@ant-design/pro-components';
|
import { PageContainer } from '@ant-design/pro-components';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import {
|
import { ConfigProvider, Empty, Table, message } from 'antd';
|
||||||
Button,
|
|
||||||
ConfigProvider,
|
|
||||||
Empty,
|
|
||||||
Input,
|
|
||||||
Space,
|
|
||||||
Table,
|
|
||||||
message
|
|
||||||
} from 'antd';
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import {
|
import {
|
||||||
@@ -34,6 +23,7 @@ import {
|
|||||||
updateCredential
|
updateCredential
|
||||||
} from './apis';
|
} from './apis';
|
||||||
import AddModal from './components/add-credential';
|
import AddModal from './components/add-credential';
|
||||||
|
import { ProviderValueMap } from './config';
|
||||||
import {
|
import {
|
||||||
CredentialFormData as FormData,
|
CredentialFormData as FormData,
|
||||||
CredentialListItem as ListItem
|
CredentialListItem as ListItem
|
||||||
@@ -99,26 +89,22 @@ const Credentials: React.FC = () => {
|
|||||||
open: boolean;
|
open: boolean;
|
||||||
action: PageActionType;
|
action: PageActionType;
|
||||||
title: string;
|
title: string;
|
||||||
|
currentData: ListItem | undefined;
|
||||||
}>({
|
}>({
|
||||||
provider: '',
|
provider: '',
|
||||||
open: false,
|
open: false,
|
||||||
action: PageAction.CREATE,
|
action: PageAction.CREATE,
|
||||||
title: ''
|
title: '',
|
||||||
|
currentData: undefined
|
||||||
});
|
});
|
||||||
const [currentData, setCurrentData] = useState<ListItem | undefined>(
|
|
||||||
undefined
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleAddCredential = (value: string) => {
|
const handleAddCredential = () => {
|
||||||
const title =
|
|
||||||
value === 'digital_ocean'
|
|
||||||
? 'Add Digital Ocean Credential'
|
|
||||||
: 'Add Kubernetes Credential';
|
|
||||||
setOpenModalStatus({
|
setOpenModalStatus({
|
||||||
provider: value,
|
provider: ProviderValueMap.DigitalOcean,
|
||||||
open: true,
|
open: true,
|
||||||
action: PageAction.CREATE,
|
action: PageAction.CREATE,
|
||||||
title: title
|
title: 'Add Cloud Credential',
|
||||||
|
currentData: undefined
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -132,7 +118,7 @@ const Credentials: React.FC = () => {
|
|||||||
data: {
|
data: {
|
||||||
...params
|
...params
|
||||||
},
|
},
|
||||||
id: currentData!.id
|
id: openModalStatus.currentData!.id
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await createCredential({ data: params });
|
await createCredential({ data: params });
|
||||||
@@ -151,12 +137,12 @@ const Credentials: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleEditUser = (row: ListItem) => {
|
const handleEditUser = (row: ListItem) => {
|
||||||
setCurrentData(row);
|
|
||||||
setOpenModalStatus({
|
setOpenModalStatus({
|
||||||
provider: row.provider,
|
provider: row.provider,
|
||||||
open: true,
|
open: true,
|
||||||
action: PageAction.EDIT,
|
action: PageAction.EDIT,
|
||||||
title: `Edit ${row.provider} Credential`
|
title: `Edit ${row.name} Credential`,
|
||||||
|
currentData: row
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -180,11 +166,6 @@ const Credentials: React.FC = () => {
|
|||||||
return <div></div>;
|
return <div></div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClickDropdown = (e: any) => {
|
|
||||||
const value = e.key;
|
|
||||||
handleAddCredential(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageContainer
|
<PageContainer
|
||||||
@@ -200,58 +181,20 @@ const Credentials: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
extra={[]}
|
extra={[]}
|
||||||
>
|
>
|
||||||
<PageTools
|
<FilterBar
|
||||||
|
showSelect={false}
|
||||||
|
showPrimaryButton={true}
|
||||||
marginBottom={22}
|
marginBottom={22}
|
||||||
left={
|
marginTop={30}
|
||||||
<Space>
|
buttonText={'Add Cloud Credential'}
|
||||||
<Input
|
handleDeleteByBatch={handleDeleteBatch}
|
||||||
placeholder={intl.formatMessage({ id: 'common.filter.name' })}
|
handleSearch={handleSearch}
|
||||||
style={{ width: 300 }}
|
handleInputChange={handleNameChange}
|
||||||
allowClear
|
handleClickPrimary={handleAddCredential}
|
||||||
onChange={handleNameChange}
|
rowSelection={rowSelection}
|
||||||
></Input>
|
width={{ input: 300 }}
|
||||||
<Button
|
></FilterBar>
|
||||||
type="text"
|
|
||||||
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
|
||||||
onClick={handleSearch}
|
|
||||||
icon={<SyncOutlined></SyncOutlined>}
|
|
||||||
></Button>
|
|
||||||
</Space>
|
|
||||||
}
|
|
||||||
right={
|
|
||||||
<Space size={20}>
|
|
||||||
<DropDownActions
|
|
||||||
menu={{
|
|
||||||
items: addActions,
|
|
||||||
onClick: handleClickDropdown
|
|
||||||
}}
|
|
||||||
trigger={['hover']}
|
|
||||||
placement="bottomRight"
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
icon={<DownOutlined></DownOutlined>}
|
|
||||||
type="primary"
|
|
||||||
iconPosition="end"
|
|
||||||
>
|
|
||||||
Add Credential
|
|
||||||
</Button>
|
|
||||||
</DropDownActions>
|
|
||||||
<Button
|
|
||||||
icon={<DeleteOutlined />}
|
|
||||||
danger
|
|
||||||
onClick={handleDeleteBatch}
|
|
||||||
disabled={!rowSelection.selectedRowKeys.length}
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
{intl?.formatMessage?.({ id: 'common.button.delete' })}
|
|
||||||
{rowSelection.selectedRowKeys.length > 0 && (
|
|
||||||
<span>({rowSelection.selectedRowKeys?.length})</span>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
}
|
|
||||||
></PageTools>
|
|
||||||
<ConfigProvider renderEmpty={renderEmpty}>
|
<ConfigProvider renderEmpty={renderEmpty}>
|
||||||
<Table
|
<Table
|
||||||
dataSource={dataSource.dataList}
|
dataSource={dataSource.dataList}
|
||||||
@@ -355,7 +298,7 @@ const Credentials: React.FC = () => {
|
|||||||
open={openModalStatus.open}
|
open={openModalStatus.open}
|
||||||
action={openModalStatus.action}
|
action={openModalStatus.action}
|
||||||
title={openModalStatus.title}
|
title={openModalStatus.title}
|
||||||
data={currentData}
|
currentData={openModalStatus.currentData}
|
||||||
onCancel={handleModalCancel}
|
onCancel={handleModalCancel}
|
||||||
onOk={handleModalOk}
|
onOk={handleModalOk}
|
||||||
></AddModal>
|
></AddModal>
|
||||||
|
|||||||
@@ -52,9 +52,11 @@ export async function queryModelsList(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function queryGPUList(params?: Global.SearchParams) {
|
export async function queryGPUList<T extends Record<string, any>>(
|
||||||
|
params?: Global.SearchParams & T
|
||||||
|
) {
|
||||||
return request<Global.PageResponse<GPUListItem>>(`/gpu-devices`, {
|
return request<Global.PageResponse<GPUListItem>>(`/gpu-devices`, {
|
||||||
methos: 'GET',
|
method: 'GET',
|
||||||
params
|
params
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -172,7 +174,7 @@ export async function queryModelScopeModels(
|
|||||||
config?: any
|
config?: any
|
||||||
) {
|
) {
|
||||||
const tagsCriterion = params.tags?.map((tag: string) => {
|
const tagsCriterion = params.tags?.map((tag: string) => {
|
||||||
return { category: 'libraries', predicate: 'contains', values: [tag] };
|
return { category: 'tags', predicate: 'contains', values: [tag] };
|
||||||
});
|
});
|
||||||
const tasksCriterion = params.tasks?.map((task: string) => {
|
const tasksCriterion = params.tasks?.map((task: string) => {
|
||||||
return { category: 'tasks', predicate: 'contains', values: [task] };
|
return { category: 'tasks', predicate: 'contains', values: [task] };
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ import vllmConfig from '../config/vllm-config';
|
|||||||
import dataformStyles from '../style/data-form.less';
|
import dataformStyles from '../style/data-form.less';
|
||||||
import GPUCard from './gpu-card';
|
import GPUCard from './gpu-card';
|
||||||
import Performance from './performance';
|
import Performance from './performance';
|
||||||
import Scaling from './scaling';
|
|
||||||
|
|
||||||
interface AdvanceConfigProps {
|
interface AdvanceConfigProps {
|
||||||
isGGUF: boolean;
|
isGGUF: boolean;
|
||||||
@@ -460,19 +459,19 @@ const AdvanceConfig: React.FC<AdvanceConfigProps> = (props) => {
|
|||||||
forceRender: true,
|
forceRender: true,
|
||||||
children: <Performance></Performance>
|
children: <Performance></Performance>
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
key: '3',
|
// key: '3',
|
||||||
label: (
|
// label: (
|
||||||
<span
|
// <span
|
||||||
style={{ fontWeight: 'var(--font-weight-medium)' }}
|
// style={{ fontWeight: 'var(--font-weight-medium)' }}
|
||||||
className="font-size-14"
|
// className="font-size-14"
|
||||||
>
|
// >
|
||||||
Scaling
|
// Scaling
|
||||||
</span>
|
// </span>
|
||||||
),
|
// ),
|
||||||
forceRender: true,
|
// forceRender: true,
|
||||||
children: <Scaling></Scaling>
|
// children: <Scaling></Scaling>
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
key: '1',
|
key: '1',
|
||||||
label: (
|
label: (
|
||||||
|
|||||||
@@ -6,17 +6,13 @@ import { useIntl } from '@umijs/max';
|
|||||||
import { Form } from 'antd';
|
import { Form } from 'antd';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { forwardRef, useImperativeHandle } from 'react';
|
import React, { forwardRef, useImperativeHandle } from 'react';
|
||||||
import {
|
import { backendOptionsMap, excludeFields, sourceOptions } from '../config';
|
||||||
backendOptionsMap,
|
|
||||||
excludeFields,
|
|
||||||
modelSourceMap,
|
|
||||||
sourceOptions
|
|
||||||
} from '../config';
|
|
||||||
import { FormInnerContext } from '../config/form-context';
|
import { FormInnerContext } from '../config/form-context';
|
||||||
import { FormData, SourceType } from '../config/types';
|
import { FormData, SourceType } from '../config/types';
|
||||||
import CatalogFrom from '../forms/catalog';
|
import CatalogFrom from '../forms/catalog';
|
||||||
import HuggingFaceForm from '../forms/hugging-face';
|
import HuggingFaceForm from '../forms/hugging-face';
|
||||||
import LocalPathForm from '../forms/local-path';
|
import LocalPathForm from '../forms/local-path';
|
||||||
|
import { useGenerateGPUOptions } from '../hooks/use-form-initial-values';
|
||||||
import AdvanceConfig from './advance-config';
|
import AdvanceConfig from './advance-config';
|
||||||
|
|
||||||
interface DataFormProps {
|
interface DataFormProps {
|
||||||
@@ -29,8 +25,7 @@ interface DataFormProps {
|
|||||||
sourceDisable?: boolean;
|
sourceDisable?: boolean;
|
||||||
backendOptions?: Global.BaseOption<string>[];
|
backendOptions?: Global.BaseOption<string>[];
|
||||||
sourceList?: Global.BaseOption<string>[];
|
sourceList?: Global.BaseOption<string>[];
|
||||||
gpuOptions: any[];
|
clusterList: Global.BaseOption<number>[];
|
||||||
modelFileOptions?: any[];
|
|
||||||
fields?: string[];
|
fields?: string[];
|
||||||
onValuesChange?: (changedValues: any, allValues: any) => void;
|
onValuesChange?: (changedValues: any, allValues: any) => void;
|
||||||
onSourceChange?: (value: string) => void;
|
onSourceChange?: (value: string) => void;
|
||||||
@@ -38,11 +33,6 @@ interface DataFormProps {
|
|||||||
onBackendChange?: (value: string) => void;
|
onBackendChange?: (value: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SEARCH_SOURCE = [
|
|
||||||
modelSourceMap.huggingface_value,
|
|
||||||
modelSourceMap.modelscope_value
|
|
||||||
];
|
|
||||||
|
|
||||||
const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||||
const {
|
const {
|
||||||
action,
|
action,
|
||||||
@@ -51,13 +41,13 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
|||||||
sourceDisable = true,
|
sourceDisable = true,
|
||||||
backendOptions,
|
backendOptions,
|
||||||
sourceList,
|
sourceList,
|
||||||
gpuOptions = [],
|
clusterList = [],
|
||||||
modelFileOptions = [],
|
|
||||||
fields = ['source'],
|
fields = ['source'],
|
||||||
onSourceChange,
|
onSourceChange,
|
||||||
onValuesChange,
|
onValuesChange,
|
||||||
onOk
|
onOk
|
||||||
} = props;
|
} = props;
|
||||||
|
const { getGPUOptionList, gpuOptions } = useGenerateGPUOptions();
|
||||||
const { getRuleMessage } = useAppUtils();
|
const { getRuleMessage } = useAppUtils();
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
@@ -143,6 +133,10 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
|||||||
onSourceChange?.(val);
|
onSourceChange?.(val);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleClusterChange = (value: number) => {
|
||||||
|
getGPUOptionList({ clusterId: value });
|
||||||
|
};
|
||||||
|
|
||||||
const handleOnValuesChange = async (changedValues: any, allValues: any) => {
|
const handleOnValuesChange = async (changedValues: any, allValues: any) => {
|
||||||
const fieldName = Object.keys(changedValues)[0];
|
const fieldName = Object.keys(changedValues)[0];
|
||||||
|
|
||||||
@@ -172,6 +166,9 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
|||||||
},
|
},
|
||||||
getFieldsValue: () => {
|
getFieldsValue: () => {
|
||||||
return form.getFieldsValue();
|
return form.getFieldsValue();
|
||||||
|
},
|
||||||
|
getGPUOptionList(params: { clusterId: number }) {
|
||||||
|
getGPUOptionList(params);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -179,78 +176,96 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<FormInnerContext.Provider
|
||||||
name="deployModel"
|
value={{
|
||||||
form={form}
|
onBackendChange: handleBackendChange,
|
||||||
onFinish={handleOk}
|
onValuesChange: onValuesChange,
|
||||||
preserve={false}
|
gpuOptions: gpuOptions
|
||||||
style={{ padding: '16px 24px' }}
|
|
||||||
clearOnDestroy={true}
|
|
||||||
onValuesChange={handleOnValuesChange}
|
|
||||||
scrollToFirstError={true}
|
|
||||||
initialValues={{
|
|
||||||
replicas: 1,
|
|
||||||
source: props.source,
|
|
||||||
placement_strategy: 'spread',
|
|
||||||
cpu_offloading: true,
|
|
||||||
scheduleType: 'auto',
|
|
||||||
categories: null,
|
|
||||||
restart_on_error: true,
|
|
||||||
distributed_inference_across_workers: true,
|
|
||||||
...initialValues
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Form.Item<FormData>
|
<Form
|
||||||
name="name"
|
name="deployModel"
|
||||||
rules={[
|
form={form}
|
||||||
{
|
onFinish={handleOk}
|
||||||
required: true,
|
preserve={false}
|
||||||
message: getRuleMessage('input', 'common.table.name')
|
style={{ padding: '16px 24px' }}
|
||||||
}
|
clearOnDestroy={true}
|
||||||
]}
|
onValuesChange={handleOnValuesChange}
|
||||||
|
scrollToFirstError={true}
|
||||||
|
initialValues={{
|
||||||
|
replicas: 1,
|
||||||
|
source: props.source,
|
||||||
|
placement_strategy: 'spread',
|
||||||
|
cpu_offloading: true,
|
||||||
|
scheduleType: 'auto',
|
||||||
|
categories: null,
|
||||||
|
restart_on_error: true,
|
||||||
|
distributed_inference_across_workers: true,
|
||||||
|
...initialValues
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<SealInput.Input
|
|
||||||
label={intl.formatMessage({
|
|
||||||
id: 'common.table.name'
|
|
||||||
})}
|
|
||||||
required
|
|
||||||
></SealInput.Input>
|
|
||||||
</Form.Item>
|
|
||||||
{fields.includes('source') && (
|
|
||||||
<Form.Item<FormData>
|
<Form.Item<FormData>
|
||||||
name="source"
|
name="name"
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: getRuleMessage('select', 'models.form.source')
|
message: getRuleMessage('input', 'common.table.name')
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<SealInput.Input
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'common.table.name'
|
||||||
|
})}
|
||||||
|
required
|
||||||
|
></SealInput.Input>
|
||||||
|
</Form.Item>
|
||||||
|
{fields.includes('source') && (
|
||||||
|
<Form.Item<FormData>
|
||||||
|
name="source"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: getRuleMessage('select', 'models.form.source')
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
<SealSelect
|
||||||
|
onChange={handleOnSourceChange}
|
||||||
|
disabled={sourceDisable}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: 'models.form.source'
|
||||||
|
})}
|
||||||
|
options={sourceList ?? sourceOptions}
|
||||||
|
required
|
||||||
|
></SealSelect>
|
||||||
|
}
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<HuggingFaceForm></HuggingFaceForm>
|
||||||
|
<LocalPathForm></LocalPathForm>
|
||||||
|
|
||||||
|
<Form.Item<FormData>
|
||||||
|
name="cluster_id"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: getRuleMessage('select', 'Cluster', false)
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
<SealSelect
|
<SealSelect
|
||||||
onChange={handleOnSourceChange}
|
onChange={handleClusterChange}
|
||||||
disabled={sourceDisable}
|
label="Cluster"
|
||||||
label={intl.formatMessage({
|
options={clusterList}
|
||||||
id: 'models.form.source'
|
|
||||||
})}
|
|
||||||
options={sourceList ?? sourceOptions}
|
|
||||||
required
|
required
|
||||||
></SealSelect>
|
></SealSelect>
|
||||||
}
|
}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
{/* <Form.Item name="backend" rules={[{ required: true }]}>
|
||||||
|
|
||||||
<FormInnerContext.Provider
|
|
||||||
value={{
|
|
||||||
onBackendChange: handleBackendChange,
|
|
||||||
onValuesChange: onValuesChange,
|
|
||||||
gpuOptions: gpuOptions
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<HuggingFaceForm></HuggingFaceForm>
|
|
||||||
<LocalPathForm></LocalPathForm>
|
|
||||||
</FormInnerContext.Provider>
|
|
||||||
{/* <Form.Item name="backend" rules={[{ required: true }]}>
|
|
||||||
<SealSelect
|
<SealSelect
|
||||||
required
|
required
|
||||||
onChange={handleBackendChange}
|
onChange={handleBackendChange}
|
||||||
@@ -291,25 +306,26 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
|||||||
}
|
}
|
||||||
></SealSelect>
|
></SealSelect>
|
||||||
</Form.Item> */}
|
</Form.Item> */}
|
||||||
<CatalogFrom></CatalogFrom>
|
<CatalogFrom></CatalogFrom>
|
||||||
<Form.Item<FormData> name="description">
|
<Form.Item<FormData> name="description">
|
||||||
<SealInput.TextArea
|
<SealInput.TextArea
|
||||||
scaleSize={true}
|
scaleSize={true}
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: 'common.table.description'
|
id: 'common.table.description'
|
||||||
})}
|
})}
|
||||||
></SealInput.TextArea>
|
></SealInput.TextArea>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<AdvanceConfig
|
<AdvanceConfig
|
||||||
form={form}
|
form={form}
|
||||||
gpuOptions={gpuOptions}
|
gpuOptions={gpuOptions}
|
||||||
isGGUF={isGGUF}
|
isGGUF={isGGUF}
|
||||||
action={action}
|
action={action}
|
||||||
source={props.source}
|
source={props.source}
|
||||||
backendOptions={backendOptions}
|
backendOptions={backendOptions}
|
||||||
handleBackendChange={handleBackendChange}
|
handleBackendChange={handleBackendChange}
|
||||||
></AdvanceConfig>
|
></AdvanceConfig>
|
||||||
</Form>
|
</Form>
|
||||||
|
</FormInnerContext.Provider>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import ModalFooter from '@/components/modal-footer';
|
|||||||
import GSDrawer from '@/components/scroller-modal/gs-drawer';
|
import GSDrawer from '@/components/scroller-modal/gs-drawer';
|
||||||
import { PageActionType } from '@/config/types';
|
import { PageActionType } from '@/config/types';
|
||||||
import { createAxiosToken } from '@/hooks/use-chunk-request';
|
import { createAxiosToken } from '@/hooks/use-chunk-request';
|
||||||
|
import { ProviderValueMap } from '@/pages/cluster-management/config';
|
||||||
import { CloseOutlined } from '@ant-design/icons';
|
import { CloseOutlined } from '@ant-design/icons';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Button } from 'antd';
|
import { Button } from 'antd';
|
||||||
@@ -17,11 +18,8 @@ import {
|
|||||||
} from '../config';
|
} from '../config';
|
||||||
import { FormContext } from '../config/form-context';
|
import { FormContext } from '../config/form-context';
|
||||||
import { CatalogSpec, FormData, ListItem, SourceType } from '../config/types';
|
import { CatalogSpec, FormData, ListItem, SourceType } from '../config/types';
|
||||||
import {
|
import { useCheckCompatibility } from '../hooks';
|
||||||
checkOnlyAscendNPU,
|
import useFormInitialValues from '../hooks/use-form-initial-values';
|
||||||
useCheckCompatibility,
|
|
||||||
useGenerateFormEditInitialValues
|
|
||||||
} from '../hooks';
|
|
||||||
import ColumnWrapper from './column-wrapper';
|
import ColumnWrapper from './column-wrapper';
|
||||||
import CompatibilityAlert from './compatible-alert';
|
import CompatibilityAlert from './compatible-alert';
|
||||||
import DataForm from './data-form';
|
import DataForm from './data-form';
|
||||||
@@ -104,10 +102,9 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
|||||||
handleOnValuesChange,
|
handleOnValuesChange,
|
||||||
warningStatus
|
warningStatus
|
||||||
} = useCheckCompatibility();
|
} = useCheckCompatibility();
|
||||||
|
const { getClusterList, clusterList } = useFormInitialValues();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { getGPUList } = useGenerateFormEditInitialValues();
|
|
||||||
const form = useRef<any>({});
|
const form = useRef<any>({});
|
||||||
const [gpuOptions, setGpuOptions] = useState<any[]>([]);
|
|
||||||
const [isGGUF, setIsGGUF] = useState<boolean>(false);
|
const [isGGUF, setIsGGUF] = useState<boolean>(false);
|
||||||
const [sourceList, setSourceList] = useState<any[]>([]);
|
const [sourceList, setSourceList] = useState<any[]>([]);
|
||||||
const [backendList, setBackendList] = useState<any[]>([]);
|
const [backendList, setBackendList] = useState<any[]>([]);
|
||||||
@@ -161,15 +158,6 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
|||||||
return EmbeddingRerankFirstQuant.includes(_.toUpper(data.quantOption));
|
return EmbeddingRerankFirstQuant.includes(_.toUpper(data.quantOption));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
data.backend === backendOptionsMap.llamaBox &&
|
|
||||||
checkOnlyAscendNPU(gpuOptions)
|
|
||||||
) {
|
|
||||||
return hasF16Ref.current
|
|
||||||
? AscendNPUQuant_F16.includes(_.toUpper(data.quantOption))
|
|
||||||
: AscendNPUQuant_Q8.includes(_.toUpper(data.quantOption));
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultQuant.includes(_.toUpper(data.quantOption));
|
return defaultQuant.includes(_.toUpper(data.quantOption));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -360,6 +348,15 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
|||||||
handleCheckFormData();
|
handleCheckFormData();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const initClusterId = () => {
|
||||||
|
const cluster_id =
|
||||||
|
clusterList?.find((item) => item.provider === ProviderValueMap.Custom)
|
||||||
|
?.value || clusterList?.[0]?.value;
|
||||||
|
|
||||||
|
console.log('cluster_id:', cluster_id);
|
||||||
|
return cluster_id;
|
||||||
|
};
|
||||||
|
|
||||||
const fetchSpecData = async () => {
|
const fetchSpecData = async () => {
|
||||||
try {
|
try {
|
||||||
axiosToken.current?.cancel?.();
|
axiosToken.current?.cancel?.();
|
||||||
@@ -407,7 +404,10 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
|||||||
size: defaultSpec.size,
|
size: defaultSpec.size,
|
||||||
backend: defaultSpec.backend
|
backend: defaultSpec.backend
|
||||||
});
|
});
|
||||||
initFormDataBySource(defaultSpec);
|
initFormDataBySource({
|
||||||
|
...defaultSpec,
|
||||||
|
cluster_id: initClusterId()
|
||||||
|
});
|
||||||
|
|
||||||
const name = _.toLower(current.name).replace(/\s/g, '-') || '';
|
const name = _.toLower(current.name).replace(/\s/g, '-') || '';
|
||||||
form.current.setFieldValue('name', name);
|
form.current.setFieldValue('name', name);
|
||||||
@@ -420,6 +420,7 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
|||||||
const allValues = generateSubmitData({
|
const allValues = generateSubmitData({
|
||||||
...defaultSpec,
|
...defaultSpec,
|
||||||
categories: _.get(current, 'categories.0', null),
|
categories: _.get(current, 'categories.0', null),
|
||||||
|
cluster_id: initClusterId(),
|
||||||
name
|
name
|
||||||
});
|
});
|
||||||
handleCheckCompatibility(allValues);
|
handleCheckCompatibility(allValues);
|
||||||
@@ -484,7 +485,9 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open) {
|
if (open) {
|
||||||
fetchSpecData();
|
setTimeout(() => {
|
||||||
|
fetchSpecData();
|
||||||
|
}, 100);
|
||||||
}
|
}
|
||||||
return () => {
|
return () => {
|
||||||
axiosToken.current?.cancel?.();
|
axiosToken.current?.cancel?.();
|
||||||
@@ -498,9 +501,7 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
|||||||
}, [open, current]);
|
}, [open, current]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getGPUList().then((data) => {
|
getClusterList();
|
||||||
setGpuOptions(data);
|
|
||||||
});
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -608,7 +609,7 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
|||||||
sourceDisable={false}
|
sourceDisable={false}
|
||||||
backendOptions={backendList}
|
backendOptions={backendList}
|
||||||
sourceList={sourceList}
|
sourceList={sourceList}
|
||||||
gpuOptions={gpuOptions}
|
clusterList={clusterList}
|
||||||
onBackendChange={handleBackendChange}
|
onBackendChange={handleBackendChange}
|
||||||
onSourceChange={handleSourceChange}
|
onSourceChange={handleSourceChange}
|
||||||
onValuesChange={onValuesChange}
|
onValuesChange={onValuesChange}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { getRequestId } from '@/atoms/models';
|
|
||||||
import ModalFooter from '@/components/modal-footer';
|
import ModalFooter from '@/components/modal-footer';
|
||||||
import GSDrawer from '@/components/scroller-modal/gs-drawer';
|
import GSDrawer from '@/components/scroller-modal/gs-drawer';
|
||||||
import { PageActionType } from '@/config/types';
|
import { PageActionType } from '@/config/types';
|
||||||
import useDeferredRequest from '@/hooks/use-deferred-request';
|
import useDeferredRequest from '@/hooks/use-deferred-request';
|
||||||
|
import { ProviderValueMap } from '@/pages/cluster-management/config';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Button } from 'antd';
|
import { Button } from 'antd';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
@@ -68,10 +68,12 @@ type AddModalProps = {
|
|||||||
source: SourceType;
|
source: SourceType;
|
||||||
isGGUF?: boolean;
|
isGGUF?: boolean;
|
||||||
width?: string | number;
|
width?: string | number;
|
||||||
gpuOptions: any[];
|
|
||||||
modelFileOptions: any[];
|
|
||||||
initialValues?: any;
|
initialValues?: any;
|
||||||
deploymentType?: 'modelList' | 'modelFiles';
|
deploymentType?: 'modelList' | 'modelFiles';
|
||||||
|
clusterList: Global.BaseOption<
|
||||||
|
number,
|
||||||
|
{ provider: string; state: string | number }
|
||||||
|
>[];
|
||||||
onOk: (values: FormData) => void;
|
onOk: (values: FormData) => void;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
};
|
};
|
||||||
@@ -95,7 +97,8 @@ const AddModal: FC<AddModalProps> = (props) => {
|
|||||||
action,
|
action,
|
||||||
width = 600,
|
width = 600,
|
||||||
deploymentType = 'modelList',
|
deploymentType = 'modelList',
|
||||||
initialValues
|
initialValues,
|
||||||
|
clusterList
|
||||||
} = props || {};
|
} = props || {};
|
||||||
const SEARCH_SOURCE = [
|
const SEARCH_SOURCE = [
|
||||||
modelSourceMap.huggingface_value,
|
modelSourceMap.huggingface_value,
|
||||||
@@ -113,7 +116,7 @@ const AddModal: FC<AddModalProps> = (props) => {
|
|||||||
warningStatus,
|
warningStatus,
|
||||||
submitAnyway
|
submitAnyway
|
||||||
} = useCheckCompatibility();
|
} = useCheckCompatibility();
|
||||||
const { onSelectModel } = useSelectModel({ gpuOptions: props.gpuOptions });
|
const { onSelectModel } = useSelectModel({ gpuOptions: [] });
|
||||||
const form = useRef<any>({});
|
const form = useRef<any>({});
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [selectedModel, setSelectedModel] = useState<any>({});
|
const [selectedModel, setSelectedModel] = useState<any>({});
|
||||||
@@ -161,15 +164,6 @@ const AddModal: FC<AddModalProps> = (props) => {
|
|||||||
evaluateStateRef.current = state;
|
evaluateStateRef.current = state;
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateEvaluateState = (state: EvaluateProccessType) => {
|
|
||||||
const currentRequestModelId = evaluateStateRef.current.requestModelId;
|
|
||||||
setEvaluteState({
|
|
||||||
...evaluateStateRef.current,
|
|
||||||
state
|
|
||||||
});
|
|
||||||
return currentRequestModelId;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOnValuesChange = (data: {
|
const handleOnValuesChange = (data: {
|
||||||
changedValues: any;
|
changedValues: any;
|
||||||
allValues: any;
|
allValues: any;
|
||||||
@@ -199,71 +193,6 @@ const AddModal: FC<AddModalProps> = (props) => {
|
|||||||
return categories || null;
|
return categories || null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const { run: onSelectFile } = useDeferredRequest(
|
|
||||||
async (item: any, modelInfo: any, manual?: boolean) => {
|
|
||||||
unlockWarningStatus();
|
|
||||||
|
|
||||||
const evaluateRes = await handleOnValuesChangeBefore?.({
|
|
||||||
changedValues: {},
|
|
||||||
allValues: form.current?.form?.getFieldsValue?.(),
|
|
||||||
source: props.source
|
|
||||||
});
|
|
||||||
console.log('onSelectFile:', item, modelInfo, evaluateRes);
|
|
||||||
|
|
||||||
// for cancel evaluate request case
|
|
||||||
if (!evaluateRes) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultSpec = getDefaultSpec({
|
|
||||||
evaluateResult: evaluateRes
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* do not reset backend_parameters when select a model file
|
|
||||||
*/
|
|
||||||
const formValues = form.current?.getFieldsValue?.(pickFieldsFromSpec);
|
|
||||||
|
|
||||||
form.current?.setFieldsValue?.({
|
|
||||||
..._.omit(modelInfo, ['name']),
|
|
||||||
file_name: item.fakeName,
|
|
||||||
backend_parameters:
|
|
||||||
formValues.backend_parameters?.length > 0
|
|
||||||
? formValues.backend_parameters
|
|
||||||
: defaultSpec.backend_parameters || [],
|
|
||||||
backend_version:
|
|
||||||
formValues.backend_version || defaultSpec.backend_version,
|
|
||||||
env: formValues.env || defaultSpec.env,
|
|
||||||
categories: getCategory(item)
|
|
||||||
});
|
|
||||||
},
|
|
||||||
100
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleSelectModelFile = async (
|
|
||||||
item: any,
|
|
||||||
options: { requestModelId: number; manual?: boolean }
|
|
||||||
) => {
|
|
||||||
const { requestModelId, manual } = options || {};
|
|
||||||
if (requestModelId !== getRequestId()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log('handleSelectModelFile:', item, selectedModel);
|
|
||||||
|
|
||||||
const modelInfo = onSelectModel(selectedModel, props.source);
|
|
||||||
|
|
||||||
form.current?.setFieldsValue?.({
|
|
||||||
..._.omit(modelInfo, ['name']),
|
|
||||||
file_name: item.fakeName,
|
|
||||||
categories: getCategory(item)
|
|
||||||
});
|
|
||||||
|
|
||||||
// evaluate the form data when select a model file
|
|
||||||
if (item.fakeName) {
|
|
||||||
onSelectFile(item, modelInfo, manual);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCancelFiles = () => {
|
const handleCancelFiles = () => {
|
||||||
cancelEvaluate();
|
cancelEvaluate();
|
||||||
modelFileRef.current?.cancelRequest();
|
modelFileRef.current?.cancelRequest();
|
||||||
@@ -441,22 +370,37 @@ const AddModal: FC<AddModalProps> = (props) => {
|
|||||||
onCancel?.();
|
onCancel?.();
|
||||||
}, [onCancel]);
|
}, [onCancel]);
|
||||||
|
|
||||||
|
const initClusterId = () => {
|
||||||
|
const cluster_id =
|
||||||
|
clusterList?.find((item) => item.provider === ProviderValueMap.Custom)
|
||||||
|
?.value || clusterList?.[0]?.value;
|
||||||
|
|
||||||
|
return cluster_id;
|
||||||
|
};
|
||||||
|
|
||||||
const handleOnOpen = () => {
|
const handleOnOpen = () => {
|
||||||
if (props.deploymentType === 'modelFiles') {
|
if (props.deploymentType === 'modelFiles') {
|
||||||
form.current?.form?.setFieldsValue({
|
form.current?.form?.setFieldsValue({
|
||||||
...props.initialValues
|
...props.initialValues,
|
||||||
|
cluster_id: initClusterId()
|
||||||
});
|
});
|
||||||
handleOnValuesChange?.({
|
handleOnValuesChange?.({
|
||||||
changedValues: {},
|
changedValues: {},
|
||||||
allValues: props.initialValues,
|
allValues: {
|
||||||
|
...props.initialValues,
|
||||||
|
cluster_id: initClusterId()
|
||||||
|
},
|
||||||
source: source
|
source: source
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let backend = checkOnlyAscendNPU(props.gpuOptions)
|
let backend = checkOnlyAscendNPU([])
|
||||||
? backendOptionsMap.ascendMindie
|
? backendOptionsMap.ascendMindie
|
||||||
: backendOptionsMap.vllm;
|
: backendOptionsMap.vllm;
|
||||||
|
|
||||||
form.current?.setFieldValue?.('backend', backend);
|
form.current?.setFieldsValue?.({
|
||||||
|
backend,
|
||||||
|
cluster_id: initClusterId()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -483,6 +427,9 @@ const AddModal: FC<AddModalProps> = (props) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open) {
|
if (open) {
|
||||||
handleOnOpen();
|
handleOnOpen();
|
||||||
|
form.current?.getGPUOptionList?.({
|
||||||
|
clusterId: initClusterId()
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
cancelEvaluate();
|
cancelEvaluate();
|
||||||
clearCahceFormValues();
|
clearCahceFormValues();
|
||||||
@@ -495,7 +442,7 @@ const AddModal: FC<AddModalProps> = (props) => {
|
|||||||
message: []
|
message: []
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}, [open, props.gpuOptions.length]);
|
}, [open, clusterList]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GSDrawer
|
<GSDrawer
|
||||||
@@ -534,7 +481,7 @@ const AddModal: FC<AddModalProps> = (props) => {
|
|||||||
handleOnSelectModelAfterEvaluate
|
handleOnSelectModelAfterEvaluate
|
||||||
}
|
}
|
||||||
displayEvaluateStatus={displayEvaluateStatus}
|
displayEvaluateStatus={displayEvaluateStatus}
|
||||||
gpuOptions={props.gpuOptions}
|
gpuOptions={[]}
|
||||||
></SearchModel>
|
></SearchModel>
|
||||||
</ColumnWrapper>
|
</ColumnWrapper>
|
||||||
<Separator></Separator>
|
<Separator></Separator>
|
||||||
@@ -559,8 +506,6 @@ const AddModal: FC<AddModalProps> = (props) => {
|
|||||||
value={{
|
value={{
|
||||||
isGGUF: isGGUF,
|
isGGUF: isGGUF,
|
||||||
pageAction: action,
|
pageAction: action,
|
||||||
modelFileOptions: props.modelFileOptions,
|
|
||||||
gpuOptions: props.gpuOptions,
|
|
||||||
onValuesChange: onValuesChange
|
onValuesChange: onValuesChange
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -609,12 +554,11 @@ const AddModal: FC<AddModalProps> = (props) => {
|
|||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
source={source}
|
source={source}
|
||||||
action={action}
|
action={action}
|
||||||
|
clusterList={clusterList}
|
||||||
selectedModel={selectedModel}
|
selectedModel={selectedModel}
|
||||||
onOk={handleOnOk}
|
onOk={handleOnOk}
|
||||||
ref={form}
|
ref={form}
|
||||||
isGGUF={isGGUF}
|
isGGUF={isGGUF}
|
||||||
gpuOptions={props.gpuOptions}
|
|
||||||
modelFileOptions={props.modelFileOptions}
|
|
||||||
onBackendChange={handleBackendChange}
|
onBackendChange={handleBackendChange}
|
||||||
onValuesChange={onValuesChange}
|
onValuesChange={onValuesChange}
|
||||||
></DataForm>
|
></DataForm>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import CheckboxField from '@/components/seal-form/checkbox-field';
|
|
||||||
import SealCascader from '@/components/seal-form/seal-cascader';
|
import SealCascader from '@/components/seal-form/seal-cascader';
|
||||||
import SealSelect from '@/components/seal-form/seal-select';
|
import SealSelect from '@/components/seal-form/seal-select';
|
||||||
import TooltipList from '@/components/tooltip-list';
|
import TooltipList from '@/components/tooltip-list';
|
||||||
@@ -7,8 +6,7 @@ import { useIntl } from '@umijs/max';
|
|||||||
import { Form } from 'antd';
|
import { Form } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { backendOptionsMap } from '../config';
|
import { backendOptionsMap } from '../config';
|
||||||
import { useFormContext } from '../config/form-context';
|
import { useFormContext, useFormInnerContext } from '../config/form-context';
|
||||||
import { FormData } from '../config/types';
|
|
||||||
import GPUCard from './gpu-card';
|
import GPUCard from './gpu-card';
|
||||||
|
|
||||||
const scheduleTypeTips = [
|
const scheduleTypeTips = [
|
||||||
@@ -30,13 +28,8 @@ const scheduleTypeTips = [
|
|||||||
|
|
||||||
const Performance: React.FC = () => {
|
const Performance: React.FC = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const {
|
const { gpuOptions } = useFormInnerContext();
|
||||||
onValuesChange,
|
const { onValuesChange, onQuantizationChange } = useFormContext();
|
||||||
onQuantizationChange,
|
|
||||||
gpuOptions,
|
|
||||||
source,
|
|
||||||
quantizationOptions
|
|
||||||
} = useFormContext();
|
|
||||||
const { getRuleMessage } = useAppUtils();
|
const { getRuleMessage } = useAppUtils();
|
||||||
const form = Form.useFormInstance();
|
const form = Form.useFormInstance();
|
||||||
|
|
||||||
@@ -167,7 +160,7 @@ const Performance: React.FC = () => {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<div style={{ paddingBottom: 22, paddingLeft: 10 }}>
|
{/* <div style={{ paddingBottom: 22, paddingLeft: 10 }}>
|
||||||
<Form.Item<FormData>
|
<Form.Item<FormData>
|
||||||
name="optimize_long_prompt"
|
name="optimize_long_prompt"
|
||||||
valuePropName="checked"
|
valuePropName="checked"
|
||||||
@@ -192,7 +185,7 @@ const Performance: React.FC = () => {
|
|||||||
})}
|
})}
|
||||||
></CheckboxField>
|
></CheckboxField>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</div>
|
</div> */}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -45,8 +45,7 @@ const CheckboxField: React.FC<{
|
|||||||
|
|
||||||
const Scaling: React.FC = () => {
|
const Scaling: React.FC = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { onValuesChange, onQuantizationChange, source, quantizationOptions } =
|
const { onValuesChange, onQuantizationChange } = useFormContext();
|
||||||
useFormContext();
|
|
||||||
const { getRuleMessage } = useAppUtils();
|
const { getRuleMessage } = useAppUtils();
|
||||||
const form = Form.useFormInstance();
|
const form = Form.useFormInstance();
|
||||||
|
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
|
|||||||
try {
|
try {
|
||||||
const params = {
|
const params = {
|
||||||
Name: `${searchInputRef.current}`,
|
Name: `${searchInputRef.current}`,
|
||||||
tags: [],
|
tags: ['gptq'],
|
||||||
tasks: filterTaskRef.current
|
tasks: filterTaskRef.current
|
||||||
? ([ModelscopeTaskMap[filterTaskRef.current]] as string[])
|
? ([ModelscopeTaskMap[filterTaskRef.current]] as string[])
|
||||||
: [],
|
: [],
|
||||||
|
|||||||
@@ -14,11 +14,6 @@ import useTableRowSelection from '@/hooks/use-table-row-selection';
|
|||||||
import useTableSort from '@/hooks/use-table-sort';
|
import useTableSort from '@/hooks/use-table-sort';
|
||||||
import { ListItem as WorkerListItem } from '@/pages/resources/config/types';
|
import { ListItem as WorkerListItem } from '@/pages/resources/config/types';
|
||||||
import { handleBatchRequest } from '@/utils';
|
import { handleBatchRequest } from '@/utils';
|
||||||
import {
|
|
||||||
IS_FIRST_LOGIN,
|
|
||||||
readState,
|
|
||||||
writeState
|
|
||||||
} from '@/utils/localstore/index';
|
|
||||||
import {
|
import {
|
||||||
DownOutlined,
|
DownOutlined,
|
||||||
QuestionCircleOutlined,
|
QuestionCircleOutlined,
|
||||||
@@ -26,16 +21,7 @@ import {
|
|||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { PageContainer } from '@ant-design/pro-components';
|
import { PageContainer } from '@ant-design/pro-components';
|
||||||
import { useIntl, useNavigate } from '@umijs/max';
|
import { useIntl, useNavigate } from '@umijs/max';
|
||||||
import {
|
import { Button, Input, Select, Space, Tooltip, message } from 'antd';
|
||||||
Button,
|
|
||||||
Empty,
|
|
||||||
Input,
|
|
||||||
Select,
|
|
||||||
Space,
|
|
||||||
Tooltip,
|
|
||||||
Typography,
|
|
||||||
message
|
|
||||||
} from 'antd';
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
@@ -76,7 +62,7 @@ import {
|
|||||||
ModelInstanceListItem,
|
ModelInstanceListItem,
|
||||||
SourceType
|
SourceType
|
||||||
} from '../config/types';
|
} from '../config/types';
|
||||||
import { useGenerateFormEditInitialValues } from '../hooks';
|
import useFormInitialValues from '../hooks/use-form-initial-values';
|
||||||
import APIAccessInfoModal from './api-access-info';
|
import APIAccessInfoModal from './api-access-info';
|
||||||
import DeployModal from './deploy-modal';
|
import DeployModal from './deploy-modal';
|
||||||
import Instances from './instances';
|
import Instances from './instances';
|
||||||
@@ -88,6 +74,7 @@ interface ModelsProps {
|
|||||||
handleNameChange: (e: any) => void;
|
handleNameChange: (e: any) => void;
|
||||||
handleShowSizeChange?: (page: number, size: number) => void;
|
handleShowSizeChange?: (page: number, size: number) => void;
|
||||||
handlePageChange: (page: number, pageSize: number | undefined) => void;
|
handlePageChange: (page: number, pageSize: number | undefined) => void;
|
||||||
|
handleClusterChange: (value: number) => void;
|
||||||
handleDeleteSuccess: () => void;
|
handleDeleteSuccess: () => void;
|
||||||
handleCategoryChange: (val: any) => void;
|
handleCategoryChange: (val: any) => void;
|
||||||
onViewLogs: () => void;
|
onViewLogs: () => void;
|
||||||
@@ -103,29 +90,12 @@ interface ModelsProps {
|
|||||||
};
|
};
|
||||||
deleteIds?: number[];
|
deleteIds?: number[];
|
||||||
workerList: WorkerListItem[];
|
workerList: WorkerListItem[];
|
||||||
modelFileOptions: any[];
|
|
||||||
catalogList?: any[];
|
|
||||||
dataSource: ListItem[];
|
dataSource: ListItem[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
loadend: boolean;
|
loadend: boolean;
|
||||||
total: number;
|
total: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const clusterList = [
|
|
||||||
{
|
|
||||||
label: 'Custom',
|
|
||||||
value: 'custom'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Kubernetes',
|
|
||||||
value: 'kubernetes'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Digital Ocean',
|
|
||||||
value: 'digital_ocean'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const statusList = [
|
const statusList = [
|
||||||
{
|
{
|
||||||
label: 'Running',
|
label: 'Running',
|
||||||
@@ -164,34 +134,27 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
onCancelViewLogs,
|
onCancelViewLogs,
|
||||||
handleCategoryChange,
|
handleCategoryChange,
|
||||||
handleOnToggleExpandAll,
|
handleOnToggleExpandAll,
|
||||||
|
handleClusterChange,
|
||||||
onStop,
|
onStop,
|
||||||
onStart,
|
onStart,
|
||||||
modelFileOptions,
|
|
||||||
deleteIds,
|
deleteIds,
|
||||||
dataSource,
|
dataSource,
|
||||||
workerList,
|
workerList,
|
||||||
catalogList,
|
|
||||||
queryParams,
|
queryParams,
|
||||||
loading,
|
loading,
|
||||||
loadend,
|
loadend,
|
||||||
total
|
total
|
||||||
}) => {
|
}) => {
|
||||||
const { getGPUList, generateFormValues, gpuDeviceList } =
|
const { getGPUOptionList, generateFormValues, clusterList, getClusterList } =
|
||||||
useGenerateFormEditInitialValues();
|
useFormInitialValues();
|
||||||
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
|
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
|
||||||
const [updateFormInitials, setUpdateFormInitials] = useState<{
|
const [updateFormInitials, setUpdateFormInitials] = useState<{
|
||||||
gpuOptions: any[];
|
|
||||||
modelFileOptions?: any[];
|
|
||||||
data: any;
|
data: any;
|
||||||
isGGUF: boolean;
|
isGGUF: boolean;
|
||||||
}>({
|
}>({
|
||||||
gpuOptions: [],
|
|
||||||
modelFileOptions: [],
|
|
||||||
data: {},
|
data: {},
|
||||||
isGGUF: false
|
isGGUF: false
|
||||||
});
|
});
|
||||||
const [isFirstLogin, setIsFirstLogin] = useState(false);
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
|
||||||
const [expandAtom, setExpandAtom] = useAtom(modelsExpandKeysAtom);
|
const [expandAtom, setExpandAtom] = useAtom(modelsExpandKeysAtom);
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -218,17 +181,13 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
width: number | string;
|
width: number | string;
|
||||||
hasLinuxWorker?: boolean;
|
hasLinuxWorker?: boolean;
|
||||||
source: SourceType;
|
source: SourceType;
|
||||||
gpuOptions: any[];
|
|
||||||
isGGUF?: boolean;
|
isGGUF?: boolean;
|
||||||
modelFileOptions?: any[];
|
|
||||||
}>({
|
}>({
|
||||||
show: false,
|
show: false,
|
||||||
hasLinuxWorker: false,
|
hasLinuxWorker: false,
|
||||||
width: 600,
|
width: 600,
|
||||||
isGGUF: false,
|
isGGUF: false,
|
||||||
source: modelSourceMap.huggingface_value as SourceType,
|
source: modelSourceMap.huggingface_value as SourceType
|
||||||
gpuOptions: [],
|
|
||||||
modelFileOptions: []
|
|
||||||
});
|
});
|
||||||
const currentData = useRef<ListItem>({} as ListItem);
|
const currentData = useRef<ListItem>({} as ListItem);
|
||||||
const [currentInstance, setCurrentInstance] = useState<{
|
const [currentInstance, setCurrentInstance] = useState<{
|
||||||
@@ -243,17 +202,6 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
});
|
});
|
||||||
const modalRef = useRef<any>(null);
|
const modalRef = useRef<any>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!catalogList?.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const getFirstLoginState = async () => {
|
|
||||||
const is_first_login = await readState(IS_FIRST_LOGIN);
|
|
||||||
setIsFirstLogin(is_first_login);
|
|
||||||
};
|
|
||||||
getFirstLoginState();
|
|
||||||
}, [catalogList?.length]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (deleteIds?.length) {
|
if (deleteIds?.length) {
|
||||||
rowSelection.removeSelectedKey(deleteIds);
|
rowSelection.removeSelectedKey(deleteIds);
|
||||||
@@ -262,7 +210,9 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const getData = async () => {
|
const getData = async () => {
|
||||||
await getGPUList();
|
const res = await getClusterList();
|
||||||
|
const clusterId = res[0]?.value;
|
||||||
|
await getGPUOptionList({ clusterId });
|
||||||
};
|
};
|
||||||
getData();
|
getData();
|
||||||
return () => {
|
return () => {
|
||||||
@@ -470,10 +420,8 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleEdit = async (row: ListItem) => {
|
const handleEdit = async (row: ListItem) => {
|
||||||
const initialValues = generateFormValues(row, gpuDeviceList.current);
|
const initialValues = generateFormValues(row, []);
|
||||||
setUpdateFormInitials({
|
setUpdateFormInitials({
|
||||||
gpuOptions: gpuDeviceList.current,
|
|
||||||
modelFileOptions: modelFileOptions,
|
|
||||||
data: initialValues,
|
data: initialValues,
|
||||||
isGGUF: row.backend === backendOptionsMap.llamaBox
|
isGGUF: row.backend === backendOptionsMap.llamaBox
|
||||||
});
|
});
|
||||||
@@ -584,9 +532,7 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
if (config) {
|
if (config) {
|
||||||
setOpenDeployModal({
|
setOpenDeployModal({
|
||||||
...config,
|
...config,
|
||||||
hasLinuxWorker: hasLinuxWorker,
|
hasLinuxWorker: hasLinuxWorker
|
||||||
gpuOptions: gpuDeviceList.current,
|
|
||||||
modelFileOptions: modelFileOptions
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -652,7 +598,10 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
span: 3,
|
span: 3,
|
||||||
render: (text: string, record: ListItem) => (
|
render: (text: string, record: ListItem) => (
|
||||||
<span className="flex flex-column" style={{ width: '100%' }}>
|
<span className="flex flex-column" style={{ width: '100%' }}>
|
||||||
{['Custom', 'Kubernetes', 'Digital Ocean'][record.id] || 'Custom'}
|
{
|
||||||
|
clusterList.find((item) => item.value === record.cluster_id)
|
||||||
|
?.label
|
||||||
|
}
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -724,31 +673,6 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
];
|
];
|
||||||
}, [sortOrder, intl, handleSelect]);
|
}, [sortOrder, intl, handleSelect]);
|
||||||
|
|
||||||
const handleOnClick = async () => {
|
|
||||||
if (isLoading) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = catalogList?.[0] || {};
|
|
||||||
try {
|
|
||||||
setIsLoading(true);
|
|
||||||
const modelData = await createModel({
|
|
||||||
data: data
|
|
||||||
});
|
|
||||||
writeState(IS_FIRST_LOGIN, false);
|
|
||||||
setIsFirstLogin(false);
|
|
||||||
setTimeout(() => {
|
|
||||||
updateExpandedRowKeys([modelData.id]);
|
|
||||||
}, 300);
|
|
||||||
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
|
||||||
handleSearch?.();
|
|
||||||
} catch (error) {
|
|
||||||
// ingore
|
|
||||||
} finally {
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleToggleExpandAll = useCallback(
|
const handleToggleExpandAll = useCallback(
|
||||||
(expanded: boolean) => {
|
(expanded: boolean) => {
|
||||||
const keys = dataSource.map((item) => item.id);
|
const keys = dataSource.map((item) => item.id);
|
||||||
@@ -760,33 +684,6 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
[dataSource]
|
[dataSource]
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderEmpty = useMemo(() => {
|
|
||||||
if (dataSource.length || !isFirstLogin || !catalogList?.length) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="flex-column justify-center flex-center"
|
|
||||||
style={{ height: 300 }}
|
|
||||||
>
|
|
||||||
<Empty description=""></Empty>
|
|
||||||
<Typography.Title level={4} style={{ marginBottom: 30 }}>
|
|
||||||
{intl.formatMessage({ id: 'models.table.list.empty' })}
|
|
||||||
</Typography.Title>
|
|
||||||
<div>
|
|
||||||
<Button type="primary" onClick={handleOnClick} loading={isLoading}>
|
|
||||||
<span
|
|
||||||
className="flex-center"
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: intl.formatMessage({ id: 'models.table.list.getStart' })
|
|
||||||
}}
|
|
||||||
></span>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}, [dataSource.length, isFirstLogin, isLoading, intl]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageContainer
|
<PageContainer
|
||||||
@@ -831,17 +728,9 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
style={{ width: 160 }}
|
style={{ width: 160 }}
|
||||||
size="large"
|
size="large"
|
||||||
maxTagCount={1}
|
maxTagCount={1}
|
||||||
|
onChange={handleClusterChange}
|
||||||
options={clusterList}
|
options={clusterList}
|
||||||
></Select>
|
></Select>
|
||||||
<Select
|
|
||||||
allowClear
|
|
||||||
showSearch={false}
|
|
||||||
placeholder="Running Replicas"
|
|
||||||
style={{ width: 140 }}
|
|
||||||
size="large"
|
|
||||||
maxTagCount={1}
|
|
||||||
options={statusList}
|
|
||||||
></Select>
|
|
||||||
<Button
|
<Button
|
||||||
type="text"
|
type="text"
|
||||||
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
||||||
@@ -918,6 +807,7 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
action={PageAction.EDIT}
|
action={PageAction.EDIT}
|
||||||
title={intl.formatMessage({ id: 'models.title.edit' })}
|
title={intl.formatMessage({ id: 'models.title.edit' })}
|
||||||
updateFormInitials={updateFormInitials}
|
updateFormInitials={updateFormInitials}
|
||||||
|
clusterList={clusterList}
|
||||||
onCancel={handleModalCancel}
|
onCancel={handleModalCancel}
|
||||||
onOk={handleModalOk}
|
onOk={handleModalOk}
|
||||||
></UpdateModel>
|
></UpdateModel>
|
||||||
@@ -929,8 +819,7 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
width={openDeployModal.width}
|
width={openDeployModal.width}
|
||||||
isGGUF={openDeployModal.isGGUF}
|
isGGUF={openDeployModal.isGGUF}
|
||||||
hasLinuxWorker={openDeployModal.hasLinuxWorker}
|
hasLinuxWorker={openDeployModal.hasLinuxWorker}
|
||||||
gpuOptions={openDeployModal.gpuOptions}
|
clusterList={clusterList}
|
||||||
modelFileOptions={openDeployModal.modelFileOptions || []}
|
|
||||||
onCancel={handleDeployModalCancel}
|
onCancel={handleDeployModalCancel}
|
||||||
onOk={handleCreateModel}
|
onOk={handleCreateModel}
|
||||||
></DeployModal>
|
></DeployModal>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import ModalFooter from '@/components/modal-footer';
|
import ModalFooter from '@/components/modal-footer';
|
||||||
import SealInput from '@/components/seal-form/seal-input';
|
import SealInput from '@/components/seal-form/seal-input';
|
||||||
import SealSelect from '@/components/seal-form/seal-select';
|
import SealSelect from '@/components/seal-form/seal-select';
|
||||||
import TooltipList from '@/components/tooltip-list';
|
|
||||||
import { PageAction } from '@/config';
|
import { PageAction } from '@/config';
|
||||||
import { PageActionType } from '@/config/types';
|
import { PageActionType } from '@/config/types';
|
||||||
import useAppUtils from '@/hooks/use-app-utils';
|
import useAppUtils from '@/hooks/use-app-utils';
|
||||||
@@ -10,9 +9,7 @@ import { Button, Form, Modal } from 'antd';
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { useEffect, useMemo, useRef } from 'react';
|
import React, { useEffect, useMemo, useRef } from 'react';
|
||||||
import {
|
import {
|
||||||
backendLabelMap,
|
|
||||||
backendOptionsMap,
|
backendOptionsMap,
|
||||||
backendTipsList,
|
|
||||||
updateExcludeFields as excludeFields,
|
updateExcludeFields as excludeFields,
|
||||||
getSourceRepoConfigValue,
|
getSourceRepoConfigValue,
|
||||||
modelSourceMap,
|
modelSourceMap,
|
||||||
@@ -24,6 +21,7 @@ import { FormData, ListItem } from '../config/types';
|
|||||||
import HuggingFaceForm from '../forms/hugging-face';
|
import HuggingFaceForm from '../forms/hugging-face';
|
||||||
import LocalPathForm from '../forms/local-path';
|
import LocalPathForm from '../forms/local-path';
|
||||||
import { useCheckCompatibility } from '../hooks';
|
import { useCheckCompatibility } from '../hooks';
|
||||||
|
import { useGenerateGPUOptions } from '../hooks/use-form-initial-values';
|
||||||
import AdvanceConfig from './advance-config';
|
import AdvanceConfig from './advance-config';
|
||||||
import ColumnWrapper from './column-wrapper';
|
import ColumnWrapper from './column-wrapper';
|
||||||
import CompatibilityAlert from './compatible-alert';
|
import CompatibilityAlert from './compatible-alert';
|
||||||
@@ -34,9 +32,12 @@ type AddModalProps = {
|
|||||||
open: boolean;
|
open: boolean;
|
||||||
updateFormInitials: {
|
updateFormInitials: {
|
||||||
data?: ListItem;
|
data?: ListItem;
|
||||||
gpuOptions: any[];
|
|
||||||
isGGUF: boolean;
|
isGGUF: boolean;
|
||||||
};
|
};
|
||||||
|
clusterList: Global.BaseOption<
|
||||||
|
number,
|
||||||
|
{ provider: string; state: string | number }
|
||||||
|
>[];
|
||||||
onOk: (values: FormData) => void;
|
onOk: (values: FormData) => void;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
};
|
};
|
||||||
@@ -48,7 +49,8 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
|
|||||||
open,
|
open,
|
||||||
onOk,
|
onOk,
|
||||||
onCancel,
|
onCancel,
|
||||||
updateFormInitials: { gpuOptions, isGGUF, data: formData }
|
clusterList,
|
||||||
|
updateFormInitials: { isGGUF, data: formData }
|
||||||
} = props || {};
|
} = props || {};
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const {
|
const {
|
||||||
@@ -58,12 +60,17 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
|
|||||||
checkTokenRef,
|
checkTokenRef,
|
||||||
warningStatus
|
warningStatus
|
||||||
} = useCheckCompatibility();
|
} = useCheckCompatibility();
|
||||||
|
const { getGPUOptionList, gpuOptions } = useGenerateGPUOptions();
|
||||||
|
|
||||||
const { getRuleMessage } = useAppUtils();
|
const { getRuleMessage } = useAppUtils();
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const submitAnyway = useRef<boolean>(false);
|
const submitAnyway = useRef<boolean>(false);
|
||||||
const originFormData = useRef<any>(null);
|
const originFormData = useRef<any>(null);
|
||||||
|
|
||||||
|
const handleClusterChange = (value: number) => {
|
||||||
|
getGPUOptionList({ clusterId: value });
|
||||||
|
};
|
||||||
|
|
||||||
const setOriginalFormData = () => {
|
const setOriginalFormData = () => {
|
||||||
if (!originFormData.current) {
|
if (!originFormData.current) {
|
||||||
originFormData.current = _.cloneDeep(formData);
|
originFormData.current = _.cloneDeep(formData);
|
||||||
@@ -336,48 +343,53 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
|
|||||||
onValuesChange: handleManulOnValuesChange
|
onValuesChange: handleManulOnValuesChange
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Form
|
<FormInnerContext.Provider
|
||||||
name="updateModalForm"
|
value={{
|
||||||
form={form}
|
onBackendChange: handleBackendChange,
|
||||||
onFinish={handleOk}
|
gpuOptions: gpuOptions
|
||||||
onValuesChange={onValuesChange}
|
|
||||||
scrollToFirstError={true}
|
|
||||||
preserve={false}
|
|
||||||
clearOnDestroy={true}
|
|
||||||
initialValues={{
|
|
||||||
...formData
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
padding: 'var(--ant-modal-content-padding)',
|
|
||||||
paddingBlock: 0
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Form.Item<FormData>
|
<Form
|
||||||
name="name"
|
name="updateModalForm"
|
||||||
rules={[
|
form={form}
|
||||||
{
|
onFinish={handleOk}
|
||||||
required: true,
|
onValuesChange={onValuesChange}
|
||||||
message: getRuleMessage('input', 'common.table.name')
|
scrollToFirstError={true}
|
||||||
}
|
preserve={false}
|
||||||
]}
|
clearOnDestroy={true}
|
||||||
|
initialValues={{
|
||||||
|
...formData
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
padding: 'var(--ant-modal-content-padding)',
|
||||||
|
paddingBlock: 0
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<SealInput.Input
|
<Form.Item<FormData>
|
||||||
label={intl.formatMessage({
|
name="name"
|
||||||
id: 'common.table.name'
|
rules={[
|
||||||
})}
|
{
|
||||||
required
|
required: true,
|
||||||
></SealInput.Input>
|
message: getRuleMessage('input', 'common.table.name')
|
||||||
</Form.Item>
|
}
|
||||||
<Form.Item<FormData>
|
]}
|
||||||
name="source"
|
>
|
||||||
rules={[
|
<SealInput.Input
|
||||||
{
|
label={intl.formatMessage({
|
||||||
required: true,
|
id: 'common.table.name'
|
||||||
message: getRuleMessage('select', 'models.form.source')
|
})}
|
||||||
}
|
required
|
||||||
]}
|
></SealInput.Input>
|
||||||
>
|
</Form.Item>
|
||||||
{action === PageAction.EDIT && (
|
<Form.Item<FormData>
|
||||||
|
name="source"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: getRuleMessage('select', 'models.form.source')
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
<SealSelect
|
<SealSelect
|
||||||
disabled={true}
|
disabled={true}
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
@@ -386,82 +398,46 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
|
|||||||
options={sourceOptions}
|
options={sourceOptions}
|
||||||
required
|
required
|
||||||
></SealSelect>
|
></SealSelect>
|
||||||
)}
|
</Form.Item>
|
||||||
</Form.Item>
|
|
||||||
<FormInnerContext.Provider
|
|
||||||
value={{
|
|
||||||
onBackendChange: handleBackendChange,
|
|
||||||
gpuOptions: gpuOptions
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<HuggingFaceForm></HuggingFaceForm>
|
<HuggingFaceForm></HuggingFaceForm>
|
||||||
<LocalPathForm></LocalPathForm>
|
<LocalPathForm></LocalPathForm>
|
||||||
</FormInnerContext.Provider>
|
<Form.Item<FormData>
|
||||||
<Form.Item name="backend" rules={[{ required: true }]}>
|
name="cluster_id"
|
||||||
<SealSelect
|
rules={[
|
||||||
required
|
|
||||||
onChange={handleAsyncBackendChange}
|
|
||||||
label={intl.formatMessage({ id: 'models.form.backend' })}
|
|
||||||
description={<TooltipList list={backendTipsList}></TooltipList>}
|
|
||||||
options={[
|
|
||||||
{
|
{
|
||||||
label: backendLabelMap[backendOptionsMap.llamaBox],
|
required: true,
|
||||||
value: backendOptionsMap.llamaBox,
|
message: getRuleMessage('select', 'Cluster', false)
|
||||||
disabled:
|
|
||||||
formData?.source === modelSourceMap.local_path_value
|
|
||||||
? false
|
|
||||||
: !isGGUF
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: backendLabelMap[backendOptionsMap.vllm],
|
|
||||||
value: backendOptionsMap.vllm,
|
|
||||||
disabled:
|
|
||||||
formData?.source === modelSourceMap.local_path_value ||
|
|
||||||
isVllmOrAscend
|
|
||||||
? false
|
|
||||||
: isGGUF
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: backendLabelMap[backendOptionsMap.ascendMindie],
|
|
||||||
value: backendOptionsMap.ascendMindie,
|
|
||||||
disabled:
|
|
||||||
formData?.source === modelSourceMap.local_path_value ||
|
|
||||||
isVllmOrAscend
|
|
||||||
? false
|
|
||||||
: isGGUF
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: backendLabelMap[backendOptionsMap.voxBox],
|
|
||||||
value: backendOptionsMap.voxBox,
|
|
||||||
disabled:
|
|
||||||
formData?.source !== modelSourceMap.local_path_value ||
|
|
||||||
!isVllmOrAscend
|
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
disabled={
|
>
|
||||||
action === PageAction.EDIT &&
|
{
|
||||||
formData?.source !== modelSourceMap.local_path_value &&
|
<SealSelect
|
||||||
!isVllmOrAscend
|
onChange={handleClusterChange}
|
||||||
|
label="Cluster"
|
||||||
|
options={clusterList}
|
||||||
|
required
|
||||||
|
></SealSelect>
|
||||||
}
|
}
|
||||||
></SealSelect>
|
</Form.Item>
|
||||||
</Form.Item>
|
<Form.Item<FormData> name="description">
|
||||||
<Form.Item<FormData> name="description">
|
<SealInput.TextArea
|
||||||
<SealInput.TextArea
|
scaleSize={true}
|
||||||
scaleSize={true}
|
label={intl.formatMessage({
|
||||||
label={intl.formatMessage({
|
id: 'common.table.description'
|
||||||
id: 'common.table.description'
|
})}
|
||||||
})}
|
></SealInput.TextArea>
|
||||||
></SealInput.TextArea>
|
</Form.Item>
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<AdvanceConfig
|
<AdvanceConfig
|
||||||
form={form}
|
form={form}
|
||||||
gpuOptions={gpuOptions}
|
gpuOptions={gpuOptions}
|
||||||
action={PageAction.EDIT}
|
action={PageAction.EDIT}
|
||||||
source={formData?.source || ''}
|
source={formData?.source || ''}
|
||||||
isGGUF={formData?.backend === backendOptionsMap.llamaBox}
|
isGGUF={formData?.backend === backendOptionsMap.llamaBox}
|
||||||
></AdvanceConfig>
|
></AdvanceConfig>
|
||||||
</Form>
|
</Form>
|
||||||
|
</FormInnerContext.Provider>
|
||||||
</FormContext.Provider>
|
</FormContext.Provider>
|
||||||
</ColumnWrapper>
|
</ColumnWrapper>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ interface FormContextProps {
|
|||||||
pageAction: PageActionType;
|
pageAction: PageActionType;
|
||||||
sizeOptions?: Global.BaseOption<number>[];
|
sizeOptions?: Global.BaseOption<number>[];
|
||||||
quantizationOptions?: Global.BaseOption<string>[];
|
quantizationOptions?: Global.BaseOption<string>[];
|
||||||
modelFileOptions?: any[];
|
|
||||||
gpuOptions?: any[];
|
gpuOptions?: any[];
|
||||||
onSizeChange?: (val: number) => void;
|
onSizeChange?: (val: number) => void;
|
||||||
onQuantizationChange?: (val: string) => void;
|
onQuantizationChange?: (val: string) => void;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export interface ListItem {
|
|||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
id: number;
|
id: number;
|
||||||
|
cluster_id: number;
|
||||||
local_path?: string;
|
local_path?: string;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
@@ -69,6 +70,7 @@ export interface FormData {
|
|||||||
description: string;
|
description: string;
|
||||||
optimize_long_prompt: boolean;
|
optimize_long_prompt: boolean;
|
||||||
enable_speculative_decoding: boolean;
|
enable_speculative_decoding: boolean;
|
||||||
|
cluster_id: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ComputedResourceClaim {
|
interface ComputedResourceClaim {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ type AddModalProps = {
|
|||||||
width?: string | number;
|
width?: string | number;
|
||||||
hasLinuxWorker?: boolean;
|
hasLinuxWorker?: boolean;
|
||||||
workersList: Global.BaseOption<number>[];
|
workersList: Global.BaseOption<number>[];
|
||||||
|
workerOptions: any[];
|
||||||
onOk: (values: FormData) => void;
|
onOk: (values: FormData) => void;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
};
|
};
|
||||||
@@ -33,7 +34,8 @@ const DownloadModel: React.FC<AddModalProps> = (props) => {
|
|||||||
onCancel,
|
onCancel,
|
||||||
hasLinuxWorker,
|
hasLinuxWorker,
|
||||||
source,
|
source,
|
||||||
width = 600
|
width = 600,
|
||||||
|
workerOptions
|
||||||
} = props || {};
|
} = props || {};
|
||||||
const SEARCH_SOURCE = [
|
const SEARCH_SOURCE = [
|
||||||
modelSourceMap.huggingface_value,
|
modelSourceMap.huggingface_value,
|
||||||
@@ -227,6 +229,7 @@ const DownloadModel: React.FC<AddModalProps> = (props) => {
|
|||||||
onOk={handleOk}
|
onOk={handleOk}
|
||||||
source={source}
|
source={source}
|
||||||
workersList={workersList}
|
workersList={workersList}
|
||||||
|
workerOptions={workerOptions}
|
||||||
></TargetForm>
|
></TargetForm>
|
||||||
</>
|
</>
|
||||||
</ColumnWrapper>
|
</ColumnWrapper>
|
||||||
|
|||||||
@@ -13,11 +13,12 @@ interface TargetFormProps {
|
|||||||
ref?: any;
|
ref?: any;
|
||||||
workersList: Global.BaseOption<number>[];
|
workersList: Global.BaseOption<number>[];
|
||||||
source: string;
|
source: string;
|
||||||
|
workerOptions: any[];
|
||||||
onOk: (values: any) => void;
|
onOk: (values: any) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TargetForm: React.FC<TargetFormProps> = forwardRef((props, ref) => {
|
const TargetForm: React.FC<TargetFormProps> = forwardRef((props, ref) => {
|
||||||
const { onOk, source, workersList } = props;
|
const { onOk, source, workersList, workerOptions } = props;
|
||||||
const { getRuleMessage } = useAppUtils();
|
const { getRuleMessage } = useAppUtils();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
@@ -115,13 +116,24 @@ const TargetForm: React.FC<TargetFormProps> = forwardRef((props, ref) => {
|
|||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{
|
<SealSelect
|
||||||
<SealSelect
|
label="Worker"
|
||||||
label="Worker"
|
options={workersList}
|
||||||
options={workersList}
|
required
|
||||||
required
|
></SealSelect>
|
||||||
></SealSelect>
|
{/* <SealCascader
|
||||||
}
|
required
|
||||||
|
showSearch
|
||||||
|
expandTrigger="hover"
|
||||||
|
multiple={false}
|
||||||
|
popupClassName="cascader-popup-wrapper gpu-selector"
|
||||||
|
maxTagCount={1}
|
||||||
|
label="Worker"
|
||||||
|
options={workerOptions}
|
||||||
|
showCheckedStrategy="SHOW_CHILD"
|
||||||
|
value={form.getFieldValue(['gpu_selector', 'gpu_ids'])}
|
||||||
|
getPopupContainer={(triggerNode) => triggerNode.parentNode}
|
||||||
|
></SealCascader> */}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{source !== modelSourceMap.local_path_value && (
|
{source !== modelSourceMap.local_path_value && (
|
||||||
<Form.Item<FormData>
|
<Form.Item<FormData>
|
||||||
|
|||||||
@@ -1,30 +1,19 @@
|
|||||||
import { createAxiosToken } from '@/hooks/use-chunk-request';
|
import { createAxiosToken } from '@/hooks/use-chunk-request';
|
||||||
import { queryModelFilesList, queryWorkersList } from '@/pages/resources/apis';
|
import { queryModelFilesList } from '@/pages/resources/apis';
|
||||||
import {
|
|
||||||
WorkerStatusMap,
|
|
||||||
WorkerStatusMapValue
|
|
||||||
} from '@/pages/resources/config';
|
|
||||||
import { ListItem as WorkerListItem } from '@/pages/resources/config/types';
|
import { ListItem as WorkerListItem } from '@/pages/resources/config/types';
|
||||||
import { convertFileSize } from '@/utils';
|
import { convertFileSize } from '@/utils';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { useDebounceFn } from 'ahooks';
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { evaluationsModelSpec, queryGPUList } from '../apis';
|
import { evaluationsModelSpec } from '../apis';
|
||||||
import {
|
import {
|
||||||
backendOptionsMap,
|
backendOptionsMap,
|
||||||
getSourceRepoConfigValue,
|
getSourceRepoConfigValue,
|
||||||
modelSourceMap,
|
modelSourceMap,
|
||||||
modelTaskMap,
|
modelTaskMap
|
||||||
setSourceRepoConfigValue
|
|
||||||
} from '../config';
|
} from '../config';
|
||||||
import { handleRecognizeAudioModel } from '../config/audio-catalog';
|
import { handleRecognizeAudioModel } from '../config/audio-catalog';
|
||||||
import {
|
import { EvaluateResult, FormData } from '../config/types';
|
||||||
EvaluateResult,
|
|
||||||
FormData,
|
|
||||||
GPUListItem,
|
|
||||||
ListItem
|
|
||||||
} from '../config/types';
|
|
||||||
|
|
||||||
export type MessageStatus = {
|
export type MessageStatus = {
|
||||||
show: boolean;
|
show: boolean;
|
||||||
@@ -41,123 +30,6 @@ export type WarningStausOptions = {
|
|||||||
override?: boolean;
|
override?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGenerateFormEditInitialValues = () => {
|
|
||||||
const gpuDeviceList = useRef<any[]>([]);
|
|
||||||
const workerList = useRef<any[]>([]);
|
|
||||||
|
|
||||||
const generateCascaderOptions = (
|
|
||||||
list: GPUListItem[],
|
|
||||||
workerList: WorkerListItem[]
|
|
||||||
) => {
|
|
||||||
// pick the worker fields from gpuList
|
|
||||||
const workerFields = new Set(['worker_name', 'worker_id', 'worker_ip']);
|
|
||||||
|
|
||||||
// generate a map for workerList by name to data
|
|
||||||
const workerDataMap = new Map<string, WorkerListItem>();
|
|
||||||
for (const worker of workerList) {
|
|
||||||
workerDataMap.set(worker.name, worker);
|
|
||||||
}
|
|
||||||
|
|
||||||
const workersMap = new Map<string, GPUListItem[]>();
|
|
||||||
for (const gpu of list) {
|
|
||||||
if (!workersMap.has(gpu.worker_name)) {
|
|
||||||
workersMap.set(gpu.worker_name, []);
|
|
||||||
}
|
|
||||||
workersMap.get(gpu.worker_name)!.push(gpu);
|
|
||||||
}
|
|
||||||
|
|
||||||
const gpuSelectorList = Array.from(workersMap.entries()).map(
|
|
||||||
([workerName, items]) => {
|
|
||||||
const firstItem = items[0];
|
|
||||||
const currentState = workerDataMap.get(workerName)?.state || '';
|
|
||||||
const disDisabled = WorkerStatusMap.ready !== currentState;
|
|
||||||
return {
|
|
||||||
label: disDisabled
|
|
||||||
? `${workerName} [${WorkerStatusMapValue[currentState]}]`
|
|
||||||
: workerName,
|
|
||||||
value: workerName,
|
|
||||||
parent: true,
|
|
||||||
disabled: disDisabled,
|
|
||||||
children: items
|
|
||||||
.map((item) => ({
|
|
||||||
label: item.name,
|
|
||||||
value: item.id,
|
|
||||||
index: item.index,
|
|
||||||
...Object.fromEntries(
|
|
||||||
Object.entries(item).filter(([key]) => !workerFields.has(key))
|
|
||||||
)
|
|
||||||
}))
|
|
||||||
.sort((a, b) => a.index - b.index),
|
|
||||||
...Object.fromEntries(
|
|
||||||
Object.entries(firstItem).filter(([key]) => workerFields.has(key))
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return gpuSelectorList;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getGPUList = async () => {
|
|
||||||
const [gpuData, workerData] = await Promise.all([
|
|
||||||
queryGPUList({ page: 1, perPage: 100 }),
|
|
||||||
queryWorkersList({ page: 1, perPage: 100 })
|
|
||||||
]);
|
|
||||||
const gpuList = generateCascaderOptions(gpuData.items, workerData.items);
|
|
||||||
|
|
||||||
gpuDeviceList.current = gpuList;
|
|
||||||
workerList.current = workerData.items;
|
|
||||||
|
|
||||||
return gpuList;
|
|
||||||
};
|
|
||||||
|
|
||||||
const generateGPUSelector = (data: any, gpuOptions: any[]) => {
|
|
||||||
const gpu_ids = _.get(data, 'gpu_selector.gpu_ids', []);
|
|
||||||
if (gpu_ids.length === 0) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const valueMap = new Map<string, string>();
|
|
||||||
gpuOptions?.forEach((item) => {
|
|
||||||
item.children?.forEach((child: any) => {
|
|
||||||
valueMap.set(child.value, item.value);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const gpuids: string[][] = gpu_ids
|
|
||||||
.map((id: string) => {
|
|
||||||
const parent = valueMap.get(id);
|
|
||||||
return parent ? [parent, id] : null;
|
|
||||||
})
|
|
||||||
.filter(Boolean) as string[][];
|
|
||||||
|
|
||||||
return data.backend === backendOptionsMap.voxBox ? gpuids[0] : gpuids;
|
|
||||||
};
|
|
||||||
|
|
||||||
const generateFormValues = (data: ListItem, gpuOptions: any[]) => {
|
|
||||||
const result = setSourceRepoConfigValue(data?.source || '', data);
|
|
||||||
|
|
||||||
const formData = {
|
|
||||||
...result.values,
|
|
||||||
categories: data?.categories?.length ? data.categories[0] : null,
|
|
||||||
scheduleType: data?.gpu_selector ? 'manual' : 'auto',
|
|
||||||
gpu_selector: data?.gpu_selector?.gpu_ids?.length
|
|
||||||
? {
|
|
||||||
gpu_ids: generateGPUSelector(data, gpuOptions)
|
|
||||||
}
|
|
||||||
: null
|
|
||||||
};
|
|
||||||
return formData;
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
getGPUList,
|
|
||||||
generateFormValues,
|
|
||||||
gpuDeviceList,
|
|
||||||
workerList
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useGenerateModelFileOptions = () => {
|
export const useGenerateModelFileOptions = () => {
|
||||||
const getModelFileList = async () => {
|
const getModelFileList = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -514,25 +386,6 @@ export const useCheckCompatibility = () => {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const checkRequiredValue = (allValues: any) => {
|
|
||||||
const { scheduleType } = allValues;
|
|
||||||
const gpuIds = allValues.gpu_selector?.gpu_ids || [];
|
|
||||||
|
|
||||||
const noLocalValue =
|
|
||||||
allValues.source === modelSourceMap.local_path_value &&
|
|
||||||
!allValues.local_path;
|
|
||||||
|
|
||||||
const noOllamaValue =
|
|
||||||
allValues.source === modelSourceMap.ollama_library_value &&
|
|
||||||
!allValues.ollama_library_model_name;
|
|
||||||
|
|
||||||
if (scheduleType === 'manual') {
|
|
||||||
return !gpuIds.length || noLocalValue || noOllamaValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return noLocalValue || noOllamaValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
const clearCahceFormValues = () => {
|
const clearCahceFormValues = () => {
|
||||||
cacheFormValuesRef.current = {};
|
cacheFormValuesRef.current = {};
|
||||||
};
|
};
|
||||||
@@ -585,11 +438,6 @@ export const useCheckCompatibility = () => {
|
|||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
const { run: debounceHandleValuesChange } = useDebounceFn(
|
|
||||||
handleOnValuesChange,
|
|
||||||
{ wait: 500 }
|
|
||||||
);
|
|
||||||
|
|
||||||
const cancelEvaluate = () => {
|
const cancelEvaluate = () => {
|
||||||
// update the requestId to cancel the current evaluation
|
// update the requestId to cancel the current evaluation
|
||||||
updateRequestId();
|
updateRequestId();
|
||||||
|
|||||||
@@ -0,0 +1,255 @@
|
|||||||
|
import { queryClusterList } from '@/pages/cluster-management/apis';
|
||||||
|
import { ClusterListItem } from '@/pages/cluster-management/config/types';
|
||||||
|
import { queryWorkersList } from '@/pages/resources/apis';
|
||||||
|
import {
|
||||||
|
WorkerStatusMap,
|
||||||
|
WorkerStatusMapValue
|
||||||
|
} from '@/pages/resources/config';
|
||||||
|
import { ListItem as WorkerListItem } from '@/pages/resources/config/types';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { queryGPUList } from '../apis';
|
||||||
|
import { backendOptionsMap, setSourceRepoConfigValue } from '../config';
|
||||||
|
import { GPUListItem, ListItem } from '../config/types';
|
||||||
|
|
||||||
|
interface CascaderOption {
|
||||||
|
label: string;
|
||||||
|
value: string | number;
|
||||||
|
parent?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
index?: number;
|
||||||
|
children?: CascaderOption[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useGenerateGPUOptions = () => {
|
||||||
|
const [gpuOptions, setGpuOptions] = useState<CascaderOption[]>([]);
|
||||||
|
|
||||||
|
const generateCascaderGPUOptions = (
|
||||||
|
gpuList: GPUListItem[],
|
||||||
|
workerList: WorkerListItem[]
|
||||||
|
) => {
|
||||||
|
// pick the worker fields from gpuList
|
||||||
|
const workerFields = new Set(['worker_name', 'worker_id', 'worker_ip']);
|
||||||
|
|
||||||
|
// generate a map for workerList by name to data
|
||||||
|
const workerDataMap = new Map<string, WorkerListItem>();
|
||||||
|
for (const worker of workerList) {
|
||||||
|
workerDataMap.set(worker.name, worker);
|
||||||
|
}
|
||||||
|
|
||||||
|
const workersMap = new Map<string, GPUListItem[]>();
|
||||||
|
for (const gpu of gpuList) {
|
||||||
|
if (!workersMap.has(gpu.worker_name)) {
|
||||||
|
workersMap.set(gpu.worker_name, []);
|
||||||
|
}
|
||||||
|
workersMap.get(gpu.worker_name)!.push(gpu);
|
||||||
|
}
|
||||||
|
|
||||||
|
const gpuSelectorList = Array.from(workersMap.entries()).map(
|
||||||
|
([workerName, items]) => {
|
||||||
|
const firstItem = items[0];
|
||||||
|
const currentState = workerDataMap.get(workerName)?.state || '';
|
||||||
|
const disDisabled = WorkerStatusMap.ready !== currentState;
|
||||||
|
return {
|
||||||
|
label: disDisabled
|
||||||
|
? `${workerName} [${WorkerStatusMapValue[currentState]}]`
|
||||||
|
: workerName,
|
||||||
|
value: workerName,
|
||||||
|
parent: true,
|
||||||
|
disabled: disDisabled,
|
||||||
|
children: items
|
||||||
|
.map((item) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
index: item.index,
|
||||||
|
...Object.fromEntries(
|
||||||
|
Object.entries(item).filter(([key]) => !workerFields.has(key))
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
.sort((a, b) => a.index - b.index),
|
||||||
|
...Object.fromEntries(
|
||||||
|
Object.entries(firstItem).filter(([key]) => workerFields.has(key))
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return gpuSelectorList;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getGPUOptionList = async (params?: { clusterId: number }) => {
|
||||||
|
const { clusterId } = params || {};
|
||||||
|
const [gpuData, workerData] = await Promise.all([
|
||||||
|
queryGPUList({
|
||||||
|
page: 1,
|
||||||
|
perPage: 100,
|
||||||
|
cluster_id: clusterId
|
||||||
|
}),
|
||||||
|
queryWorkersList({
|
||||||
|
page: 1,
|
||||||
|
perPage: 100,
|
||||||
|
cluster_id: clusterId
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
const gpuList = generateCascaderGPUOptions(gpuData.items, workerData.items);
|
||||||
|
setGpuOptions(gpuList);
|
||||||
|
return gpuList;
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
getGPUOptionList,
|
||||||
|
gpuOptions
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGenerateWorkerOptions = () => {
|
||||||
|
const [workerOptions, setWorkerOptions] = useState<CascaderOption[]>([]);
|
||||||
|
const [clusterList, setClusterList] = useState<
|
||||||
|
Global.BaseOption<number, { provider: string; state: string | number }>[]
|
||||||
|
>([]);
|
||||||
|
const [workersList, setWorkersList] = useState<
|
||||||
|
Global.BaseOption<
|
||||||
|
number,
|
||||||
|
{ state: string; labels: Record<string, string> }
|
||||||
|
>[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
|
const generateCascaderWorkerOptions = (
|
||||||
|
workerList: WorkerListItem[],
|
||||||
|
clusterList: ClusterListItem[]
|
||||||
|
) => {
|
||||||
|
const options = clusterList.map((cluster) => ({
|
||||||
|
label: cluster.name,
|
||||||
|
value: cluster.id,
|
||||||
|
parent: true,
|
||||||
|
children: workerList
|
||||||
|
.filter((worker) => worker.cluster_id === cluster.id)
|
||||||
|
.map((worker) => ({
|
||||||
|
disabled: WorkerStatusMap.ready !== worker.state,
|
||||||
|
label: worker.name,
|
||||||
|
value: worker.id
|
||||||
|
}))
|
||||||
|
}));
|
||||||
|
setWorkerOptions(options);
|
||||||
|
return options;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDataList = async (): Promise<
|
||||||
|
[WorkerListItem[], ClusterListItem[]]
|
||||||
|
> => {
|
||||||
|
const [workerRes, clusterRes] = await Promise.all([
|
||||||
|
queryWorkersList({
|
||||||
|
page: 1,
|
||||||
|
perPage: 100
|
||||||
|
}),
|
||||||
|
queryClusterList({
|
||||||
|
page: 1,
|
||||||
|
perPage: 100
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
const workerList = workerRes.items || ([] as WorkerListItem[]);
|
||||||
|
const clusterList = clusterRes.items || ([] as ClusterListItem[]);
|
||||||
|
return [workerList, clusterList];
|
||||||
|
};
|
||||||
|
|
||||||
|
const getWorkerOptionList = async () => {
|
||||||
|
const data = await getDataList();
|
||||||
|
const [workerList, clusterList] = data;
|
||||||
|
generateCascaderWorkerOptions(workerList, clusterList);
|
||||||
|
setWorkersList(
|
||||||
|
workerList.map((item) => ({
|
||||||
|
state: item.state,
|
||||||
|
label: item.name,
|
||||||
|
value: item.id
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
setClusterList(
|
||||||
|
clusterList.map((item) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
provider: item.provider,
|
||||||
|
state: item.state
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
getWorkerOptionList,
|
||||||
|
workerOptions,
|
||||||
|
clusterList,
|
||||||
|
workersList
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function useFormInitialValues() {
|
||||||
|
const { getGPUOptionList } = useGenerateGPUOptions();
|
||||||
|
|
||||||
|
const [clusterList, setClusterList] = useState<
|
||||||
|
Global.BaseOption<number, { provider: string; state: string | number }>[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
|
const getClusterList = async (): Promise<Global.BaseOption<number>[]> => {
|
||||||
|
try {
|
||||||
|
const response = await queryClusterList({
|
||||||
|
page: 1,
|
||||||
|
perPage: 100
|
||||||
|
});
|
||||||
|
const list = response.items.map((item) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
provider: item.provider,
|
||||||
|
state: item.state
|
||||||
|
}));
|
||||||
|
setClusterList(list);
|
||||||
|
return list;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch cluster list:', error);
|
||||||
|
setClusterList([]);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateGPUSelector = (data: any, gpuOptions: any[]) => {
|
||||||
|
const gpu_ids = _.get(data, 'gpu_selector.gpu_ids', []);
|
||||||
|
if (gpu_ids.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const valueMap = new Map<string, string>();
|
||||||
|
gpuOptions?.forEach((item) => {
|
||||||
|
item.children?.forEach((child: any) => {
|
||||||
|
valueMap.set(child.value, item.value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const gpuids: string[][] = gpu_ids
|
||||||
|
.map((id: string) => {
|
||||||
|
const parent = valueMap.get(id);
|
||||||
|
return parent ? [parent, id] : null;
|
||||||
|
})
|
||||||
|
.filter(Boolean) as string[][];
|
||||||
|
|
||||||
|
return data.backend === backendOptionsMap.voxBox ? gpuids[0] : gpuids;
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateFormValues = (data: ListItem, gpuOptions: any[]) => {
|
||||||
|
const result = setSourceRepoConfigValue(data?.source || '', data);
|
||||||
|
|
||||||
|
const formData = {
|
||||||
|
...result.values,
|
||||||
|
categories: data?.categories?.length ? data.categories[0] : null,
|
||||||
|
scheduleType: data?.gpu_selector ? 'manual' : 'auto',
|
||||||
|
gpu_selector: data?.gpu_selector?.gpu_ids?.length
|
||||||
|
? {
|
||||||
|
gpu_ids: generateGPUSelector(data, gpuOptions)
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
};
|
||||||
|
return formData;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
getGPUOptionList,
|
||||||
|
generateFormValues,
|
||||||
|
getClusterList,
|
||||||
|
clusterList
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -3,26 +3,19 @@ import useSetChunkRequest from '@/hooks/use-chunk-request';
|
|||||||
import useUpdateChunkedList from '@/hooks/use-update-chunk-list';
|
import useUpdateChunkedList from '@/hooks/use-update-chunk-list';
|
||||||
import { queryWorkersList } from '@/pages/resources/apis';
|
import { queryWorkersList } from '@/pages/resources/apis';
|
||||||
import { ListItem as WokerListItem } from '@/pages/resources/config/types';
|
import { ListItem as WokerListItem } from '@/pages/resources/config/types';
|
||||||
import { IS_FIRST_LOGIN, readState } from '@/utils/localstore';
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import qs from 'query-string';
|
import qs from 'query-string';
|
||||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
MODELS_API,
|
MODELS_API,
|
||||||
MODEL_INSTANCE_API,
|
MODEL_INSTANCE_API,
|
||||||
queryCatalogItemSpec,
|
|
||||||
queryCatalogList,
|
|
||||||
queryModelsInstances,
|
queryModelsInstances,
|
||||||
queryModelsList
|
queryModelsList
|
||||||
} from './apis';
|
} from './apis';
|
||||||
import TableList from './components/table-list';
|
import TableList from './components/table-list';
|
||||||
import { backendOptionsMap } from './config';
|
|
||||||
import { ListItem } from './config/types';
|
import { ListItem } from './config/types';
|
||||||
import { useGenerateModelFileOptions } from './hooks';
|
|
||||||
|
|
||||||
const Models: React.FC = () => {
|
const Models: React.FC = () => {
|
||||||
const { getModelFileList, generateModelFileOptions } =
|
|
||||||
useGenerateModelFileOptions();
|
|
||||||
const { setChunkRequest, createAxiosToken } = useSetChunkRequest();
|
const { setChunkRequest, createAxiosToken } = useSetChunkRequest();
|
||||||
const { setChunkRequest: setModelInstanceChunkRequest } =
|
const { setChunkRequest: setModelInstanceChunkRequest } =
|
||||||
useSetChunkRequest();
|
useSetChunkRequest();
|
||||||
@@ -41,9 +34,7 @@ const Models: React.FC = () => {
|
|||||||
total: 0
|
total: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
const [catalogList, setCatalogList] = useState<any[]>([]);
|
|
||||||
const [workerList, setWorkerList] = useState<WokerListItem[]>([]);
|
const [workerList, setWorkerList] = useState<WokerListItem[]>([]);
|
||||||
const [modelFileOptions, setModelFileOptions] = useState<any[]>([]);
|
|
||||||
const chunkRequedtRef = useRef<any>();
|
const chunkRequedtRef = useRef<any>();
|
||||||
const chunkInstanceRequedtRef = useRef<any>();
|
const chunkInstanceRequedtRef = useRef<any>();
|
||||||
const isPageHidden = useRef(false);
|
const isPageHidden = useRef(false);
|
||||||
@@ -168,20 +159,20 @@ const Models: React.FC = () => {
|
|||||||
[queryParams]
|
[queryParams]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleQueryChange = (params: any) => {
|
||||||
|
setQueryParams({
|
||||||
|
...queryParams,
|
||||||
|
...params
|
||||||
|
});
|
||||||
|
fetchData({ query: { ...queryParams, ...params } });
|
||||||
|
};
|
||||||
|
|
||||||
const handlePageChange = useCallback(
|
const handlePageChange = useCallback(
|
||||||
(page: number, pageSize: number | undefined) => {
|
(page: number, pageSize: number | undefined) => {
|
||||||
setQueryParams({
|
handleQueryChange({
|
||||||
...queryParams,
|
|
||||||
page: page,
|
page: page,
|
||||||
perPage: pageSize || 10
|
perPage: pageSize || 10
|
||||||
});
|
});
|
||||||
fetchData({
|
|
||||||
query: {
|
|
||||||
...queryParams,
|
|
||||||
page: page,
|
|
||||||
perPage: pageSize || 10
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[queryParams]
|
[queryParams]
|
||||||
);
|
);
|
||||||
@@ -269,18 +260,10 @@ const Models: React.FC = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const debounceUpdateFilter = _.debounce((e: any) => {
|
const debounceUpdateFilter = _.debounce((e: any) => {
|
||||||
setQueryParams({
|
handleQueryChange({
|
||||||
...queryParams,
|
|
||||||
page: 1,
|
page: 1,
|
||||||
search: e.target.value
|
search: e.target.value
|
||||||
});
|
});
|
||||||
fetchData({
|
|
||||||
query: {
|
|
||||||
...queryParams,
|
|
||||||
page: 1,
|
|
||||||
search: e.target.value
|
|
||||||
}
|
|
||||||
});
|
|
||||||
createModelsChunkRequest({
|
createModelsChunkRequest({
|
||||||
search: e.target.value,
|
search: e.target.value,
|
||||||
categories: queryParams.categories
|
categories: queryParams.categories
|
||||||
@@ -289,27 +272,27 @@ const Models: React.FC = () => {
|
|||||||
|
|
||||||
const handleNameChange = useCallback(debounceUpdateFilter, [queryParams]);
|
const handleNameChange = useCallback(debounceUpdateFilter, [queryParams]);
|
||||||
|
|
||||||
const handleCategoryChange = useCallback(
|
const handleCategoryChange = async (value: any) => {
|
||||||
async (value: any) => {
|
handleQueryChange({
|
||||||
setQueryParams({
|
page: 1,
|
||||||
...queryParams,
|
categories: value
|
||||||
page: 1,
|
});
|
||||||
categories: value
|
createModelsChunkRequest({
|
||||||
});
|
search: queryParams.search,
|
||||||
fetchData({
|
categories: value
|
||||||
query: {
|
});
|
||||||
...queryParams,
|
};
|
||||||
page: 1,
|
|
||||||
categories: value
|
const handleClusterChange = async (value: any) => {
|
||||||
}
|
handleQueryChange({
|
||||||
});
|
page: 1,
|
||||||
createModelsChunkRequest({
|
cluster_id: value
|
||||||
search: queryParams.search,
|
});
|
||||||
categories: value
|
createModelsChunkRequest({
|
||||||
});
|
search: queryParams.search,
|
||||||
},
|
cluster_id: value
|
||||||
[queryParams]
|
});
|
||||||
);
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let timer: any = null;
|
let timer: any = null;
|
||||||
@@ -345,55 +328,12 @@ const Models: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// get catalog list
|
|
||||||
const getCataLogList = async () => {
|
|
||||||
const isFirstLogin = readState(IS_FIRST_LOGIN);
|
|
||||||
if (!isFirstLogin) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const res: any = await queryCatalogList({
|
|
||||||
search: 'DeepSeek R1',
|
|
||||||
page: 1
|
|
||||||
});
|
|
||||||
if (!res?.items?.length) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
const name = _.toLower(res?.items[0]?.name).replace(/\s/g, '-') || '';
|
|
||||||
const catalogSpecs: any = await queryCatalogItemSpec({
|
|
||||||
id: res?.items[0]?.id
|
|
||||||
});
|
|
||||||
const list = catalogSpecs?.items?.map((item: any) => {
|
|
||||||
item.name = name;
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
const deepseekr1dstill = _.toLower('DeepSeek-R1-Distill-Qwen-1.5B');
|
|
||||||
const resultList = list?.filter((item: any) => {
|
|
||||||
return (
|
|
||||||
item.backend === backendOptionsMap.llamaBox &&
|
|
||||||
(_.toLower(item?.huggingface_repo_id)?.indexOf(deepseekr1dstill) >
|
|
||||||
-1 ||
|
|
||||||
_.toLower(item?.model_scope_model_id)?.indexOf(deepseekr1dstill) >
|
|
||||||
-1)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return resultList || [];
|
|
||||||
} catch (error) {
|
|
||||||
// ignore
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const init = async () => {
|
const init = async () => {
|
||||||
const [modelRes, workerRes, modelFileList] = await Promise.all([
|
const [modelRes, workerRes] = await Promise.all([
|
||||||
getTableData(),
|
getTableData(),
|
||||||
getWorkerList(),
|
getWorkerList()
|
||||||
getModelFileList()
|
|
||||||
]);
|
]);
|
||||||
const dataList = generateModelFileOptions(
|
|
||||||
modelFileList,
|
|
||||||
workerRes.items || []
|
|
||||||
);
|
|
||||||
setDataSource({
|
setDataSource({
|
||||||
dataList: modelRes.items || [],
|
dataList: modelRes.items || [],
|
||||||
loading: false,
|
loading: false,
|
||||||
@@ -402,7 +342,6 @@ const Models: React.FC = () => {
|
|||||||
deletedIds: []
|
deletedIds: []
|
||||||
});
|
});
|
||||||
setWorkerList(workerRes.items || []);
|
setWorkerList(workerRes.items || []);
|
||||||
setModelFileOptions(dataList);
|
|
||||||
|
|
||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
timer = setTimeout(() => {
|
timer = setTimeout(() => {
|
||||||
@@ -464,6 +403,7 @@ const Models: React.FC = () => {
|
|||||||
dataSource={dataSource.dataList}
|
dataSource={dataSource.dataList}
|
||||||
handleNameChange={handleNameChange}
|
handleNameChange={handleNameChange}
|
||||||
handleCategoryChange={handleCategoryChange}
|
handleCategoryChange={handleCategoryChange}
|
||||||
|
handleClusterChange={handleClusterChange}
|
||||||
handleSearch={handleSearch}
|
handleSearch={handleSearch}
|
||||||
handlePageChange={handlePageChange}
|
handlePageChange={handlePageChange}
|
||||||
handleDeleteSuccess={fetchData}
|
handleDeleteSuccess={fetchData}
|
||||||
@@ -478,8 +418,6 @@ const Models: React.FC = () => {
|
|||||||
total={dataSource.total}
|
total={dataSource.total}
|
||||||
deleteIds={dataSource.deletedIds}
|
deleteIds={dataSource.deletedIds}
|
||||||
workerList={workerList}
|
workerList={workerList}
|
||||||
modelFileOptions={modelFileOptions}
|
|
||||||
catalogList={catalogList}
|
|
||||||
></TableList>
|
></TableList>
|
||||||
</TableContext.Provider>
|
</TableContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ export const WORKERS_API = '/workers';
|
|||||||
export const GPU_DEVICES_API = '/gpu-devices';
|
export const GPU_DEVICES_API = '/gpu-devices';
|
||||||
export const MODEL_FILES_API = '/model-files';
|
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}`, {
|
return request<Global.PageResponse<ListItem>>(`${WORKERS_API}`, {
|
||||||
methos: 'GET',
|
method: 'GET',
|
||||||
params
|
params
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,16 @@ import ContainerInstall from './container-install';
|
|||||||
|
|
||||||
type ViewModalProps = {
|
type ViewModalProps = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
token: string;
|
registrationInfo: {
|
||||||
|
token: string;
|
||||||
|
image: string;
|
||||||
|
server_url: string;
|
||||||
|
};
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const AddWorker: React.FC<ViewModalProps> = (props) => {
|
const AddWorker: React.FC<ViewModalProps> = (props) => {
|
||||||
const { open, onCancel, token = '${token}' } = props || {};
|
const { open, onCancel, registrationInfo } = props || {};
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -30,7 +34,7 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
|
|||||||
maxContentHeight={450}
|
maxContentHeight={450}
|
||||||
footer={null}
|
footer={null}
|
||||||
>
|
>
|
||||||
<ContainerInstall token={token} />
|
<ContainerInstall registrationInfo={registrationInfo} />
|
||||||
</ScrollerModal>
|
</ScrollerModal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ import { addWorkerGuide, containerInstallOptions } from '../config';
|
|||||||
import './styles/installation.less';
|
import './styles/installation.less';
|
||||||
|
|
||||||
type ViewModalProps = {
|
type ViewModalProps = {
|
||||||
token: string;
|
registrationInfo: {
|
||||||
|
token: string;
|
||||||
|
image: string;
|
||||||
|
server_url: string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const npuOptions = [
|
const npuOptions = [
|
||||||
@@ -18,6 +22,7 @@ const npuOptions = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const AddWorker: React.FC<ViewModalProps> = (props) => {
|
const AddWorker: React.FC<ViewModalProps> = (props) => {
|
||||||
|
const { registrationInfo } = props || {};
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const origin = window.location.origin;
|
const origin = window.location.origin;
|
||||||
@@ -40,12 +45,18 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
|
|||||||
const tag = activeKey === 'cuda' ? version : `${version}-${activeKey}`;
|
const tag = activeKey === 'cuda' ? version : `${version}-${activeKey}`;
|
||||||
|
|
||||||
return commandCode?.registerWorker({
|
return commandCode?.registerWorker({
|
||||||
server: origin,
|
server: registrationInfo.server_url || origin,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
token: props.token || '${token}',
|
token: registrationInfo.token || '${token}',
|
||||||
workerip: '${workerip}'
|
workerip: '${workerip}'
|
||||||
});
|
});
|
||||||
}, [versionInfo, activeKey, props.token, npuKey]);
|
}, [
|
||||||
|
versionInfo,
|
||||||
|
activeKey,
|
||||||
|
registrationInfo.token,
|
||||||
|
registrationInfo.server_url,
|
||||||
|
npuKey
|
||||||
|
]);
|
||||||
|
|
||||||
const handleOnChange = (value: string | number) => {
|
const handleOnChange = (value: string | number) => {
|
||||||
setNpuKey(value as string);
|
setNpuKey(value as string);
|
||||||
@@ -53,7 +64,7 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container-install">
|
<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' })}{' '}
|
1. {intl.formatMessage({ id: 'resources.worker.add.step2' })}{' '}
|
||||||
<span
|
<span
|
||||||
className="font-size-12"
|
className="font-size-12"
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
} from '@/pages/llmodels/config/button-actions';
|
} from '@/pages/llmodels/config/button-actions';
|
||||||
import { SourceType } from '@/pages/llmodels/config/types';
|
import { SourceType } from '@/pages/llmodels/config/types';
|
||||||
import DownloadModal from '@/pages/llmodels/download';
|
import DownloadModal from '@/pages/llmodels/download';
|
||||||
|
import { useGenerateWorkerOptions } from '@/pages/llmodels/hooks/use-form-initial-values';
|
||||||
import { convertFileSize } from '@/utils';
|
import { convertFileSize } from '@/utils';
|
||||||
import {
|
import {
|
||||||
CheckCircleFilled,
|
CheckCircleFilled,
|
||||||
@@ -32,19 +33,14 @@ import { ConfigProvider, Empty, Table, Tag, Typography, message } from 'antd';
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import {
|
import { checkCurrentbackend } from '../../llmodels/hooks';
|
||||||
checkCurrentbackend,
|
|
||||||
useGenerateFormEditInitialValues,
|
|
||||||
useGenerateModelFileOptions
|
|
||||||
} from '../../llmodels/hooks';
|
|
||||||
import {
|
import {
|
||||||
MODEL_FILES_API,
|
MODEL_FILES_API,
|
||||||
deleteModelFile,
|
deleteModelFile,
|
||||||
downloadModelFile,
|
downloadModelFile,
|
||||||
queryModelFilesList,
|
queryModelFilesList,
|
||||||
queryWorkersList,
|
|
||||||
retryDownloadModelFile
|
retryDownloadModelFile
|
||||||
} from '../apis';
|
} from '../apis';
|
||||||
import {
|
import {
|
||||||
@@ -53,10 +49,7 @@ import {
|
|||||||
ModelfileStateMapValue,
|
ModelfileStateMapValue,
|
||||||
WorkerStatusMap
|
WorkerStatusMap
|
||||||
} from '../config';
|
} from '../config';
|
||||||
import {
|
import { ModelFile as ListItem } from '../config/types';
|
||||||
ModelFile as ListItem,
|
|
||||||
ListItem as WorkerListItem
|
|
||||||
} from '../config/types';
|
|
||||||
|
|
||||||
const { Paragraph } = Typography;
|
const { Paragraph } = Typography;
|
||||||
|
|
||||||
@@ -287,7 +280,8 @@ const ResolvedPathColumn = (props: { record: ListItem }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ModelFiles = () => {
|
const ModelFiles = () => {
|
||||||
const { getGPUList } = useGenerateFormEditInitialValues();
|
const { getWorkerOptionList, workerOptions, clusterList, workersList } =
|
||||||
|
useGenerateWorkerOptions();
|
||||||
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
|
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
|
||||||
const [modelsExpandKeys, setModelsExpandKeys] = useAtom(modelsExpandKeysAtom);
|
const [modelsExpandKeys, setModelsExpandKeys] = useAtom(modelsExpandKeysAtom);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -311,63 +305,36 @@ const ModelFiles = () => {
|
|||||||
watch: true,
|
watch: true,
|
||||||
contentForDelete: 'resources.modelfiles.modelfile'
|
contentForDelete: 'resources.modelfiles.modelfile'
|
||||||
});
|
});
|
||||||
const { getModelFileList, generateModelFileOptions } =
|
|
||||||
useGenerateModelFileOptions();
|
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { showSuccess } = useAppUtils();
|
const { showSuccess } = useAppUtils();
|
||||||
const [workersList, setWorkersList] = useState<any[]>([]);
|
|
||||||
const [downloadModalStatus, setDownlaodMoalStatus] = useState<{
|
const [downloadModalStatus, setDownlaodMoalStatus] = useState<{
|
||||||
show: boolean;
|
show: boolean;
|
||||||
width: number | string;
|
width: number | string;
|
||||||
source: string;
|
source: string;
|
||||||
hasLinuxWorker: boolean;
|
hasLinuxWorker: boolean;
|
||||||
gpuOptions: any[];
|
|
||||||
}>({
|
}>({
|
||||||
show: false,
|
show: false,
|
||||||
width: 600,
|
width: 600,
|
||||||
hasLinuxWorker: false,
|
hasLinuxWorker: false,
|
||||||
source: modelSourceMap.huggingface_value,
|
source: modelSourceMap.huggingface_value
|
||||||
gpuOptions: []
|
|
||||||
});
|
});
|
||||||
const [openDeployModal, setOpenDeployModal] = useState<{
|
const [openDeployModal, setOpenDeployModal] = useState<{
|
||||||
show: boolean;
|
show: boolean;
|
||||||
width: number | string;
|
width: number | string;
|
||||||
source: SourceType;
|
source: SourceType;
|
||||||
gpuOptions: any[];
|
|
||||||
modelFileOptions?: any[];
|
|
||||||
initialValues: any;
|
initialValues: any;
|
||||||
isGGUF?: boolean;
|
isGGUF?: boolean;
|
||||||
}>({
|
}>({
|
||||||
show: false,
|
show: false,
|
||||||
width: 600,
|
width: 600,
|
||||||
source: modelSourceMap.local_path_value as SourceType,
|
source: modelSourceMap.local_path_value as SourceType,
|
||||||
gpuOptions: [],
|
|
||||||
modelFileOptions: [],
|
|
||||||
initialValues: {},
|
initialValues: {},
|
||||||
isGGUF: false
|
isGGUF: false
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchWorkerList = async () => {
|
getWorkerOptionList();
|
||||||
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();
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const extractFileName = (name: string) => {
|
const extractFileName = (name: string) => {
|
||||||
@@ -440,16 +407,10 @@ const ModelFiles = () => {
|
|||||||
showSuccess();
|
showSuccess();
|
||||||
} else if (val === 'deploy') {
|
} else if (val === 'deploy') {
|
||||||
saveScrollHeight();
|
saveScrollHeight();
|
||||||
const [modelFileList, gpuList] = await Promise.all([
|
|
||||||
getModelFileList(),
|
const initialValues = generateInitialValues(record, []);
|
||||||
getGPUList()
|
|
||||||
]);
|
|
||||||
const dataList = generateModelFileOptions(modelFileList, workersList);
|
|
||||||
const initialValues = generateInitialValues(record, gpuList);
|
|
||||||
setOpenDeployModal({
|
setOpenDeployModal({
|
||||||
...openDeployModal,
|
...openDeployModal,
|
||||||
modelFileOptions: dataList,
|
|
||||||
gpuOptions: gpuList,
|
|
||||||
initialValues: initialValues,
|
initialValues: initialValues,
|
||||||
isGGUF: initialValues.isGGUF,
|
isGGUF: initialValues.isGGUF,
|
||||||
show: true
|
show: true
|
||||||
@@ -472,18 +433,15 @@ const ModelFiles = () => {
|
|||||||
return <div></div>;
|
return <div></div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClickDropdown = useCallback(
|
const handleClickDropdown = (item: any) => {
|
||||||
(item: any) => {
|
const config = modalConfig[item.key];
|
||||||
const config = modalConfig[item.key];
|
const hasLinuxWorker = workersList.some(
|
||||||
const hasLinuxWorker = workersList.some(
|
(worker) => _.toLower(worker.labels?.os) === 'linux'
|
||||||
(worker) => _.toLower(worker.labels?.os) === 'linux'
|
);
|
||||||
);
|
if (config) {
|
||||||
if (config) {
|
setDownlaodMoalStatus({ ...config, hasLinuxWorker });
|
||||||
setDownlaodMoalStatus({ ...config, hasLinuxWorker, gpuOptions: [] });
|
}
|
||||||
}
|
};
|
||||||
},
|
|
||||||
[workersList]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleDownloadCancel = () => {
|
const handleDownloadCancel = () => {
|
||||||
setDownlaodMoalStatus({
|
setDownlaodMoalStatus({
|
||||||
@@ -709,6 +667,7 @@ const ModelFiles = () => {
|
|||||||
width={downloadModalStatus.width}
|
width={downloadModalStatus.width}
|
||||||
hasLinuxWorker={downloadModalStatus.hasLinuxWorker}
|
hasLinuxWorker={downloadModalStatus.hasLinuxWorker}
|
||||||
workersList={readyWorkers}
|
workersList={readyWorkers}
|
||||||
|
workerOptions={workerOptions}
|
||||||
></DownloadModal>
|
></DownloadModal>
|
||||||
<DeployModal
|
<DeployModal
|
||||||
deploymentType="modelFiles"
|
deploymentType="modelFiles"
|
||||||
@@ -719,10 +678,9 @@ const ModelFiles = () => {
|
|||||||
action={PageAction.CREATE}
|
action={PageAction.CREATE}
|
||||||
source={openDeployModal.source}
|
source={openDeployModal.source}
|
||||||
width={openDeployModal.width}
|
width={openDeployModal.width}
|
||||||
gpuOptions={openDeployModal.gpuOptions}
|
|
||||||
modelFileOptions={openDeployModal.modelFileOptions || []}
|
|
||||||
initialValues={openDeployModal.initialValues}
|
initialValues={openDeployModal.initialValues}
|
||||||
isGGUF={openDeployModal.isGGUF}
|
isGGUF={openDeployModal.isGGUF}
|
||||||
|
clusterList={clusterList}
|
||||||
></DeployModal>
|
></DeployModal>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import ProgressBar from '@/components/progress-bar';
|
|||||||
import InfoColumn from '@/components/simple-table/info-column';
|
import InfoColumn from '@/components/simple-table/info-column';
|
||||||
import StatusTag from '@/components/status-tag';
|
import StatusTag from '@/components/status-tag';
|
||||||
import useTableFetch from '@/hooks/use-table-fetch';
|
import useTableFetch from '@/hooks/use-table-fetch';
|
||||||
|
import { queryClusterList } from '@/pages/cluster-management/apis';
|
||||||
import { convertFileSize } from '@/utils';
|
import { convertFileSize } from '@/utils';
|
||||||
import {
|
import {
|
||||||
CodeOutlined,
|
CodeOutlined,
|
||||||
@@ -18,7 +19,7 @@ import { PageContainer } from '@ant-design/pro-components';
|
|||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { ConfigProvider, Empty, Table, Tooltip, message } from 'antd';
|
import { ConfigProvider, Empty, Table, Tooltip, message } from 'antd';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { useCallback, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
WORKERS_API,
|
WORKERS_API,
|
||||||
deleteWorker,
|
deleteWorker,
|
||||||
@@ -27,7 +28,6 @@ import {
|
|||||||
} from '../apis';
|
} from '../apis';
|
||||||
import { WorkerStatusMapValue, status } from '../config';
|
import { WorkerStatusMapValue, status } from '../config';
|
||||||
import { Filesystem, GPUDeviceItem, ListItem } from '../config/types';
|
import { Filesystem, GPUDeviceItem, ListItem } from '../config/types';
|
||||||
import AddWorker from './add-worker';
|
|
||||||
import UpdateLabels from './update-labels';
|
import UpdateLabels from './update-labels';
|
||||||
|
|
||||||
const { Column } = Table;
|
const { Column } = Table;
|
||||||
@@ -115,6 +115,7 @@ const Workers: React.FC = () => {
|
|||||||
handlePageChange,
|
handlePageChange,
|
||||||
handleTableChange,
|
handleTableChange,
|
||||||
handleSearch,
|
handleSearch,
|
||||||
|
handleQueryChange,
|
||||||
handleNameChange
|
handleNameChange
|
||||||
} = useTableFetch<ListItem>({
|
} = useTableFetch<ListItem>({
|
||||||
fetchAPI: queryWorkersList,
|
fetchAPI: queryWorkersList,
|
||||||
@@ -125,7 +126,6 @@ const Workers: React.FC = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
const [updateLabelsData, setUpdateLabelsData] = useState<{
|
const [updateLabelsData, setUpdateLabelsData] = useState<{
|
||||||
open: boolean;
|
open: boolean;
|
||||||
data: ListItem;
|
data: ListItem;
|
||||||
@@ -133,35 +133,65 @@ const Workers: React.FC = () => {
|
|||||||
open: false,
|
open: false,
|
||||||
data: {} as ListItem
|
data: {} as ListItem
|
||||||
});
|
});
|
||||||
|
const [clusterData, setClusterData] = useState<{
|
||||||
|
list: Global.BaseOption<number>[];
|
||||||
|
data: Record<number, string>;
|
||||||
|
}>({
|
||||||
|
list: [],
|
||||||
|
data: {}
|
||||||
|
});
|
||||||
|
|
||||||
const handleAddWorker = () => {
|
const getClusterList = async () => {
|
||||||
setOpen(true);
|
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(
|
const handleUpdateLabelsOk = async (values: Record<string, any>) => {
|
||||||
async (values: Record<string, any>) => {
|
try {
|
||||||
try {
|
console.log('updateLabelsData.data', updateLabelsData.data);
|
||||||
console.log('updateLabelsData.data', updateLabelsData.data);
|
await updateWorker(updateLabelsData.data.id, {
|
||||||
await updateWorker(updateLabelsData.data.id, {
|
...updateLabelsData.data,
|
||||||
...updateLabelsData.data,
|
labels: values.labels
|
||||||
labels: values.labels
|
});
|
||||||
});
|
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
||||||
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
fetchData();
|
||||||
fetchData();
|
setUpdateLabelsData({ open: false, data: {} as ListItem });
|
||||||
setUpdateLabelsData({ open: false, data: {} as ListItem });
|
} catch (error) {
|
||||||
} catch (error) {
|
console.log('error', error);
|
||||||
console.log('error', error);
|
}
|
||||||
}
|
};
|
||||||
},
|
|
||||||
[updateLabelsData, fetchData]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleCancelUpdateLabels = useCallback(() => {
|
const handleCancelUpdateLabels = () => {
|
||||||
setUpdateLabelsData({
|
setUpdateLabelsData({
|
||||||
...updateLabelsData,
|
...updateLabelsData,
|
||||||
open: false
|
open: false
|
||||||
});
|
});
|
||||||
}, []);
|
};
|
||||||
|
|
||||||
const handleUpdateLabels = (record: ListItem) => {
|
const handleUpdateLabels = (record: ListItem) => {
|
||||||
console.log('record', record);
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageContainer
|
<PageContainer
|
||||||
@@ -232,9 +273,10 @@ const Workers: React.FC = () => {
|
|||||||
buttonText={intl.formatMessage({ id: 'resources.button.create' })}
|
buttonText={intl.formatMessage({ id: 'resources.button.create' })}
|
||||||
handleDeleteByBatch={handleDeleteBatch}
|
handleDeleteByBatch={handleDeleteBatch}
|
||||||
handleSearch={handleSearch}
|
handleSearch={handleSearch}
|
||||||
handleClickPrimary={handleAddWorker}
|
handleSelectChange={handleClusterChange}
|
||||||
handleInputChange={handleNameChange}
|
handleInputChange={handleNameChange}
|
||||||
rowSelection={rowSelection}
|
rowSelection={rowSelection}
|
||||||
|
selectOptions={clusterData.list}
|
||||||
width={{ input: 200 }}
|
width={{ input: 200 }}
|
||||||
></FilterBar>
|
></FilterBar>
|
||||||
<ConfigProvider renderEmpty={renderEmpty}>
|
<ConfigProvider renderEmpty={renderEmpty}>
|
||||||
@@ -309,7 +351,7 @@ const Workers: React.FC = () => {
|
|||||||
render={(text, record: ListItem) => {
|
render={(text, record: ListItem) => {
|
||||||
return (
|
return (
|
||||||
<AutoTooltip ghost maxWidth={240}>
|
<AutoTooltip ghost maxWidth={240}>
|
||||||
<span>digital-ocean-cluster</span>
|
<span>{clusterData.data[record.cluster_id]}</span>
|
||||||
</AutoTooltip>
|
</AutoTooltip>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
@@ -496,7 +538,6 @@ const Workers: React.FC = () => {
|
|||||||
</Table>
|
</Table>
|
||||||
</ConfigProvider>
|
</ConfigProvider>
|
||||||
<DeleteModal ref={modalRef}></DeleteModal>
|
<DeleteModal ref={modalRef}></DeleteModal>
|
||||||
<AddWorker open={open} onCancel={() => setOpen(false)}></AddWorker>
|
|
||||||
<UpdateLabels
|
<UpdateLabels
|
||||||
open={updateLabelsData.open}
|
open={updateLabelsData.open}
|
||||||
onOk={handleUpdateLabelsOk}
|
onOk={handleUpdateLabelsOk}
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ export interface ListItem {
|
|||||||
labels: Record<string, string>;
|
labels: Record<string, string>;
|
||||||
state: string;
|
state: string;
|
||||||
ip: string;
|
ip: string;
|
||||||
|
cluster_id: number;
|
||||||
state_message: string;
|
state_message: string;
|
||||||
status: {
|
status: {
|
||||||
cpu: {
|
cpu: {
|
||||||
|
|||||||
Reference in New Issue
Block a user