feat: duplicate deployment

This commit is contained in:
jialin
2026-02-04 14:58:48 +08:00
parent 5ad9ac277c
commit 05c05419fc
25 changed files with 366 additions and 220 deletions
@@ -27,7 +27,12 @@ import _ from 'lodash';
import React, { useCallback, useEffect, useMemo } from 'react';
import styled from 'styled-components';
import { MODEL_INSTANCE_API } from '../apis';
import { InstanceStatusMap, InstanceStatusMapValue, status } from '../config';
import {
InstanceStatusMap,
InstanceStatusMapValue,
modelCategoriesMap,
status
} from '../config';
import { backendOptionsMap } from '../config/backend-parameters';
import { generateSource } from '../config/button-actions';
import {
@@ -431,12 +436,18 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
const intl = useIntl();
const actionItems = useMemo(() => {
return _.filter(childActionList, (action: any) => {
if (action.key === 'benchmark') {
return (
action.status.includes(instanceData.state) &&
modelData?.categories?.includes(modelCategoriesMap.llm)
);
}
if (action.status && action.status.length > 0) {
return action.status.includes(instanceData.state);
}
return true;
});
}, [instanceData.state]);
}, [instanceData.state, modelData]);
const createFileName = (name: string) => {
const timestamp = dayjs().format('YYYY-MM-DD_HH-mm-ss');
+84 -116
View File
@@ -15,6 +15,9 @@ import useExpandedRowKeys from '@/hooks/use-expanded-row-keys';
import useTableRowSelection from '@/hooks/use-table-row-selection';
import PageBox from '@/pages/_components/page-box';
import useNoResourceResult from '@/pages/llmodels/hooks/use-no-resource-result';
import { TargetStatusValueMap } from '@/pages/model-routes/config';
import useOpenPlayground from '@/pages/model-routes/hooks/use-open-playground';
import useQueryTargets from '@/pages/model-routes/services/use-query-targets';
import { handleBatchRequest } from '@/utils';
import { DownOutlined, SearchOutlined, SyncOutlined } from '@ant-design/icons';
import { useIntl, useNavigate, useSearchParams } from '@umijs/max';
@@ -54,11 +57,10 @@ import {
ModelInstanceListItem,
SourceType
} from '../config/types';
import useEditDeployment from '../hooks/use-edit-deployment';
import useFilterStatus from '../hooks/use-filter-status';
import useFormInitialValues from '../hooks/use-form-initial-values';
import useModelsColumns from '../hooks/use-models-columns';
import AccessControlModal from './access-control-modal';
import APIAccessInfoModal from './api-access-info';
import DeployModal from './deploy-modal';
import Instances from './instances';
import UpdateModelModal from './update-modal';
@@ -140,13 +142,12 @@ const Models: React.FC<ModelsProps> = ({
const [searchParams] = useSearchParams();
const page = searchParams.get('page');
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
const [updateFormInitials, setUpdateFormInitials] = useState<{
data: any;
isGGUF: boolean;
}>({
data: {},
isGGUF: false
});
const {
openEditModalStatus,
openEditModal,
openDuplicateModal,
closeEditModal
} = useEditDeployment();
const [expandAtom, setExpandAtom] = useAtom(modelsExpandKeysAtom);
const [modelsSession, setModelsSession] = useAtom(modelsSessionAtom);
const intl = useIntl();
@@ -159,17 +160,18 @@ const Models: React.FC<ModelsProps> = ({
removeExpandedRowKey,
expandedRowKeys
} = useExpandedRowKeys(expandAtom);
const { handleOpenPlayGround } = useOpenPlayground();
const { labelRender, optionRender, handleStatusChange, statusOptions } =
useFilterStatus({
onStatusChange: onStatusChange
});
const { fetchData: fetchTargets, dataList: targetList } = useQueryTargets();
useEffect(() => {
fetchTargets({});
}, []);
const [apiAccessInfo, setAPIAccessInfo] = useState<any>({
show: false,
data: {}
});
const [openLogModal, setOpenLogModal] = useState(false);
const [openAddModal, setOpenAddModal] = useState(false);
const [openDeployModal, setOpenDeployModal] = useState<{
show: boolean;
width: number | string;
@@ -183,7 +185,6 @@ const Models: React.FC<ModelsProps> = ({
isGGUF: false,
source: modelSourceMap.huggingface_value as SourceType
});
const currentData = useRef<ListItem>({} as ListItem);
const [currentInstance, setCurrentInstance] = useState<{
url: string;
status: string;
@@ -194,17 +195,6 @@ const Models: React.FC<ModelsProps> = ({
url: '',
status: ''
});
const [openAccessControlModal, setOpenAccessControlModal] = useState<{
open: boolean;
currentData: ListItem | null;
title: string;
action: PageActionType;
}>({
open: false,
currentData: null,
title: '',
action: PageAction.CREATE
});
const modalRef = useRef<any>(null);
useEffect(() => {
@@ -223,10 +213,6 @@ const Models: React.FC<ModelsProps> = ({
};
}, []);
const setCurrentData = (data: ListItem) => {
currentData.current = data;
};
const handleOnSort = (order: TableOrder | Array<TableOrder>) => {
onTableSort?.(order);
};
@@ -253,16 +239,29 @@ const Models: React.FC<ModelsProps> = ({
};
const handleModalOk = async (data: FormData) => {
const currentData = openEditModalStatus.currentData;
try {
await updateModel({
data,
id: currentData.current?.id as number
});
setOpenAddModal(false);
message.success(intl.formatMessage({ id: 'common.message.success' }));
if (data.replicas > currentData.current?.replicas) {
updateExpandedRowKeys([currentData.current?.id, ...expandedRowKeys]);
if (currentData.realAction === PageAction.COPY) {
const modelData = await createModel({
data
});
if (data.replicas > 0) {
updateExpandedRowKeys([modelData.id, ...expandedRowKeys]);
}
}
if (currentData.realAction === PageAction.EDIT) {
await updateModel({
data,
id: currentData.row.id as number
});
if (data.replicas > currentData.row.replicas) {
updateExpandedRowKeys([currentData.row.id, ...expandedRowKeys]);
}
}
closeEditModal();
message.success(intl.formatMessage({ id: 'common.message.success' }));
setTimeout(() => {
handleSearch();
}, 150);
@@ -271,7 +270,7 @@ const Models: React.FC<ModelsProps> = ({
};
const handleModalCancel = useCallback(() => {
setOpenAddModal(false);
closeEditModal();
restoreScrollHeight();
}, []);
@@ -282,29 +281,28 @@ const Models: React.FC<ModelsProps> = ({
});
};
const handleCreateModel = useCallback(
async (data: FormData) => {
try {
console.log('data:', data, openDeployModal);
const refreshListStatus = (modelData: ListItem) => {
setTimeout(() => {
updateExpandedRowKeys([modelData.id, ...expandedRowKeys]);
}, 300);
message.success(intl.formatMessage({ id: 'common.message.success' }));
setTimeout(() => {
handleSearch?.();
}, 150);
};
const modelData = await createModel({
data
});
setOpenDeployModal({
...openDeployModal,
show: false
});
setTimeout(() => {
updateExpandedRowKeys([modelData.id, ...expandedRowKeys]);
}, 300);
message.success(intl.formatMessage({ id: 'common.message.success' }));
setTimeout(() => {
handleSearch?.();
}, 150);
} catch (error) {}
},
[openDeployModal]
);
const handleCreateModel = async (data: FormData) => {
try {
const modelData = await createModel({
data
});
setOpenDeployModal({
...openDeployModal,
show: false
});
refreshListStatus(modelData);
} catch (error) {}
};
const handleLogModalCancel = useCallback(() => {
setOpenLogModal(false);
@@ -400,28 +398,23 @@ const Models: React.FC<ModelsProps> = ({
return `${MODELS_API}/${params.id}/instances`;
}, []);
const handleEdit = async (row: ListItem) => {
const handleEdit = async (row: ListItem, realAction: PageActionType) => {
const initialValues = generateFormValues(row, []);
setUpdateFormInitials({
data: initialValues,
isGGUF: false
});
setCurrentData(row);
setOpenAddModal(true);
if (realAction === PageAction.EDIT) {
openEditModal(initialValues, row);
} else if (realAction === PageAction.COPY) {
openDuplicateModal(initialValues, row);
}
saveScrollHeight();
};
const handleViewAPIInfo = useCallback((row: ListItem) => {
setAPIAccessInfo({
show: true,
data: row
});
}, []);
const handleSelect = useMemoizedFn(async (val: any, row: ListItem) => {
try {
if (val === 'edit') {
handleEdit(row);
handleEdit(row, PageAction.EDIT);
}
if (val === 'copy') {
handleEdit(row, PageAction.COPY);
}
if (val === 'delete') {
handleDelete(row);
@@ -433,10 +426,6 @@ const Models: React.FC<ModelsProps> = ({
onStart?.();
}
if (val === 'api') {
handleViewAPIInfo(row);
}
if (val === 'stop') {
modalRef.current?.show({
content: 'models.instances',
@@ -450,13 +439,16 @@ const Models: React.FC<ModelsProps> = ({
}
});
}
if (val === 'chat') {
const targetRoute = targetList.find(
(target) =>
target.model_id === row.id &&
target.state === TargetStatusValueMap.Active
);
if (val === 'accessControl') {
setOpenAccessControlModal({
title: intl.formatMessage({ id: 'models.button.accessSettings' }),
action: PageAction.EDIT,
currentData: row,
open: true
handleOpenPlayGround({
categories: row.categories || [],
name: targetRoute?.route_name || ''
});
}
} catch (error) {
@@ -551,7 +543,8 @@ const Models: React.FC<ModelsProps> = ({
return {
handleSelect,
clusterList,
sortOrder
sortOrder,
targetList: targetList
};
}, [handleSelect, clusterList, sortOrder]);
@@ -565,14 +558,6 @@ const Models: React.FC<ModelsProps> = ({
}
});
const handleCancelAccessControl = () => {
setOpenAccessControlModal({
...openAccessControlModal,
currentData: null,
open: false
});
};
const { noResourceResult } = useNoResourceResult({
loadend: loadend,
loading: loading,
@@ -735,10 +720,10 @@ const Models: React.FC<ModelsProps> = ({
></SealTable>
</PageBox>
<UpdateModelModal
open={openAddModal}
action={PageAction.EDIT}
title={intl.formatMessage({ id: 'models.title.edit' })}
updateFormInitials={updateFormInitials}
open={openEditModalStatus.open}
action={openEditModalStatus.action}
title={openEditModalStatus.title}
currentData={openEditModalStatus.currentData}
clusterList={clusterList}
onCancel={handleModalCancel}
onOk={handleModalOk}
@@ -765,23 +750,6 @@ const Models: React.FC<ModelsProps> = ({
onCancel={handleLogModalCancel}
></ViewLogsModal>
<DeleteModal ref={modalRef}></DeleteModal>
<APIAccessInfoModal
open={apiAccessInfo.show}
data={apiAccessInfo.data}
onClose={() => {
setAPIAccessInfo({
...apiAccessInfo,
show: false
});
}}
></APIAccessInfoModal>
<AccessControlModal
onCancel={handleCancelAccessControl}
title={openAccessControlModal.title}
open={openAccessControlModal.open}
currentData={openAccessControlModal.currentData}
action={openAccessControlModal.action}
></AccessControlModal>
</>
);
};
+18 -33
View File
@@ -1,10 +1,10 @@
import ModalFooter from '@/components/modal-footer';
import GSDrawer from '@/components/scroller-modal/gs-drawer';
import { PageAction } from '@/config';
import { PageActionType } from '@/config/types';
import { useIntl } from '@umijs/max';
import { Button } from 'antd';
import _ from 'lodash';
import React, { useEffect, useMemo, useRef } from 'react';
import React, { useEffect, useRef } from 'react';
import ColumnWrapper from '../../_components/column-wrapper';
import {
DeployFormKeyMap,
@@ -22,9 +22,10 @@ type AddModalProps = {
title: string;
action: PageActionType;
open: boolean;
updateFormInitials: {
currentData: {
data: FormData;
isGGUF: boolean;
realAction?: PageActionType;
};
clusterList: Global.BaseOption<
number,
@@ -48,7 +49,7 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
onOk,
onCancel,
clusterList,
updateFormInitials: { isGGUF, data: formData }
currentData: { isGGUF, data: formData, realAction }
} = props || {};
const intl = useIntl();
const {
@@ -198,14 +199,6 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
onCancel?.();
};
const showExtraButton = useMemo(() => {
return (
warningStatus.show &&
warningStatus.type !== 'success' &&
!warningStatus.isDefault
);
}, [warningStatus.show, warningStatus.type, warningStatus.isDefault]);
useEffect(() => {
const initGPUSelector = async () => {
const gpuOptions = await formRef.current?.getGPUOptionList({
@@ -256,31 +249,23 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
}}
footer={
<>
<CompatibilityAlert
showClose={false}
onClose={() => {
setWarningStatus({
show: false,
message: ''
});
}}
warningStatus={warningStatus}
contentStyle={{ paddingInline: 0 }}
></CompatibilityAlert>
{realAction === PageAction.EDIT && (
<CompatibilityAlert
showClose={false}
onClose={() => {
setWarningStatus({
show: false,
message: ''
});
}}
warningStatus={warningStatus}
contentStyle={{ paddingInline: 0 }}
></CompatibilityAlert>
)}
<ModalFooter
style={ModalFooterStyle}
onCancel={onCancel}
onOk={handleSumit}
showOkBtn={!showExtraButton}
extra={
showExtraButton && (
<Button type="primary" onClick={handleSubmitAnyway}>
{intl.formatMessage({
id: 'models.form.submit.anyway'
})}
</Button>
)
}
></ModalFooter>
</>
}