refactor: deployments dir tree
This commit is contained in:
@@ -0,0 +1,453 @@
|
||||
import ModalFooter from '@/components/modal-footer';
|
||||
import GSDrawer from '@/components/scroller-modal/gs-drawer';
|
||||
import { PageActionType } from '@/config/types';
|
||||
import { createAxiosToken } from '@/hooks/use-chunk-request';
|
||||
import ColumnWrapper from '@/pages/_components/column-wrapper';
|
||||
import { ClusterStatusValueMap } from '@/pages/cluster-management/config';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button, message } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { queryCatalogItemSpec } from '../../apis';
|
||||
import { DeployFormKeyMap, sourceOptions } from '../../config';
|
||||
import { CatalogFormContext } from '../../config/form-context';
|
||||
import {
|
||||
CatalogSpec,
|
||||
FormData,
|
||||
ListItem,
|
||||
SourceType
|
||||
} from '../../config/types';
|
||||
import { backendOptionsMap } from '../../constants/backend-parameters';
|
||||
import DataForm from '../../forms';
|
||||
import { useCheckCompatibility } from '../../hooks';
|
||||
import useFormInitialValues from '../../hooks/use-form-initial-values';
|
||||
import { generateGPUIds } from '../../utils';
|
||||
import CompatibilityAlert from '../compatible-alert';
|
||||
|
||||
const ModesMap: Record<string, string> = {
|
||||
latency: 'models.form.mode.latency',
|
||||
standard: 'models.form.mode.baseline',
|
||||
throughput: 'models.form.mode.throughput'
|
||||
};
|
||||
|
||||
const ModesTipsMap: Record<string, string> = {
|
||||
latency: 'models.form.mode.latency.tips',
|
||||
standard: 'models.form.mode.baseline.tips',
|
||||
throughput: 'models.form.mode.throughput.tips'
|
||||
};
|
||||
|
||||
const pickFieldsFromSpec = [
|
||||
'env',
|
||||
'size',
|
||||
'source',
|
||||
'quantization',
|
||||
'backend_version',
|
||||
'backend_parameters',
|
||||
'backend',
|
||||
'extended_kv_cache',
|
||||
'speculative_config'
|
||||
];
|
||||
|
||||
type AddModalProps = {
|
||||
title: string;
|
||||
action: PageActionType;
|
||||
open: boolean;
|
||||
data?: ListItem;
|
||||
source: SourceType;
|
||||
width?: string | number;
|
||||
current?: any;
|
||||
onOk: (values: FormData) => void;
|
||||
onCancel: () => void;
|
||||
};
|
||||
|
||||
const FormWrapper = styled.div`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
maxwidth: 100%;
|
||||
`;
|
||||
|
||||
const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
const {
|
||||
title,
|
||||
open,
|
||||
onOk,
|
||||
onCancel,
|
||||
source,
|
||||
action,
|
||||
current,
|
||||
width = 600
|
||||
} = props || {};
|
||||
const {
|
||||
setWarningStatus,
|
||||
handleDoEvalute,
|
||||
cancelEvaluate,
|
||||
clearCacheFormValues,
|
||||
submitAnyway,
|
||||
handleOnValuesChange,
|
||||
warningStatus
|
||||
} = useCheckCompatibility();
|
||||
const { getClusterList, getWorkerList, clusterList } = useFormInitialValues();
|
||||
const intl = useIntl();
|
||||
const form = useRef<any>({});
|
||||
const [isGGUF, setIsGGUF] = useState<boolean>(false);
|
||||
const [sourceList, setSourceList] = useState<any[]>([]);
|
||||
const [modeList, setModeList] = useState<
|
||||
Global.BaseOption<string, { isBuiltIn: boolean; tips: string }>[]
|
||||
>([]);
|
||||
const sourceGroupMap = useRef<any>({});
|
||||
const axiosToken = useRef<any>(null);
|
||||
const selectSpecRef = useRef<CatalogSpec>({} as CatalogSpec);
|
||||
const specListRef = useRef<any[]>([]);
|
||||
const noCompatibleGPUsRef = useRef<boolean>(false);
|
||||
|
||||
const handleSumit = () => {
|
||||
form.current?.submit?.();
|
||||
};
|
||||
|
||||
const handleSubmitAnyway = async () => {
|
||||
if (noCompatibleGPUsRef.current) {
|
||||
message.error(intl.formatMessage({ id: 'models.catalog.nogpus.tips' }));
|
||||
return;
|
||||
}
|
||||
submitAnyway.current = true;
|
||||
form.current?.submit?.();
|
||||
};
|
||||
|
||||
const generateSubmitData = (formData: FormData) => {
|
||||
const gpuSelector = generateGPUIds(formData);
|
||||
const data = {
|
||||
..._.omit(selectSpecRef.current, ['name']),
|
||||
...formData,
|
||||
...gpuSelector
|
||||
};
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
const getModelSpec = (data: {
|
||||
mode?: string;
|
||||
backend: string;
|
||||
size: number;
|
||||
quantization: string;
|
||||
}) => {
|
||||
const defaultSpec = _.find(
|
||||
specListRef.current,
|
||||
(item: CatalogSpec) => item.mode === data.mode
|
||||
);
|
||||
selectSpecRef.current = defaultSpec;
|
||||
return {
|
||||
..._.pick(defaultSpec, pickFieldsFromSpec),
|
||||
categories: _.get(current, 'categories.0', null)
|
||||
};
|
||||
};
|
||||
|
||||
const initFormDataBySource = (data: CatalogSpec) => {
|
||||
selectSpecRef.current = data;
|
||||
form.current?.setFieldsValue({
|
||||
..._.omit(data, ['name']),
|
||||
categories: _.get(current, 'categories.0', null)
|
||||
});
|
||||
};
|
||||
|
||||
const handleCheckCompatibility = async (formData: FormData) => {
|
||||
// no compatible gpus, do nothing
|
||||
if (noCompatibleGPUsRef.current) {
|
||||
return;
|
||||
}
|
||||
handleDoEvalute(formData);
|
||||
};
|
||||
|
||||
const handleCheckFormData = () => {
|
||||
const values = form.current?.getFieldsValue();
|
||||
const allValues = generateSubmitData(values);
|
||||
handleCheckCompatibility(allValues);
|
||||
};
|
||||
|
||||
const handleSourceChange = (source: string) => {
|
||||
const defaultSpec = _.get(sourceGroupMap.current, `${source}.0`, {});
|
||||
initFormDataBySource(defaultSpec);
|
||||
|
||||
// set form value
|
||||
initFormDataBySource(defaultSpec);
|
||||
handleCheckFormData();
|
||||
};
|
||||
|
||||
const onValuesChange = async (changedValues: any, allValues: any) => {
|
||||
const data = {
|
||||
..._.omit(selectSpecRef.current, ['name']),
|
||||
...allValues
|
||||
};
|
||||
|
||||
// no compatible gpus, do nothing
|
||||
if (noCompatibleGPUsRef.current) {
|
||||
return;
|
||||
}
|
||||
handleOnValuesChange?.({
|
||||
changedValues,
|
||||
allValues: data,
|
||||
source: props.source
|
||||
});
|
||||
};
|
||||
|
||||
const handleBackendChange = (backend: string) => {
|
||||
handleCheckFormData();
|
||||
};
|
||||
|
||||
const initClusterId = (): number => {
|
||||
const defaultCluster = clusterList?.find((item) => item.is_default);
|
||||
if (defaultCluster) {
|
||||
return defaultCluster.value;
|
||||
}
|
||||
const cluster_id =
|
||||
clusterList?.find((item) => item.state === ClusterStatusValueMap.Ready)
|
||||
?.value || clusterList?.[0]?.value;
|
||||
|
||||
return cluster_id as number;
|
||||
};
|
||||
|
||||
const fetchSpecData = async (clusterId: number) => {
|
||||
try {
|
||||
axiosToken.current?.cancel?.();
|
||||
axiosToken.current = createAxiosToken();
|
||||
const res: any = await queryCatalogItemSpec(
|
||||
{
|
||||
id: current.id,
|
||||
cluster_id: clusterId
|
||||
},
|
||||
{
|
||||
token: axiosToken.current.token
|
||||
}
|
||||
);
|
||||
const groupList = _.groupBy(res.items, 'source');
|
||||
|
||||
const modes: string[] = res.items?.map((item: CatalogSpec) => {
|
||||
return item.mode;
|
||||
});
|
||||
|
||||
const modeDataList = [...new Set(modes)].map((key: string) => {
|
||||
return {
|
||||
label: _.get(ModesMap, key, key || ''),
|
||||
isBuiltIn: ModesMap[key] ? true : false,
|
||||
value: key,
|
||||
tips: _.get(ModesTipsMap, key, '')
|
||||
};
|
||||
});
|
||||
|
||||
sourceGroupMap.current = groupList;
|
||||
|
||||
specListRef.current = res.items;
|
||||
|
||||
const sources = _.filter(sourceOptions, (item: any) => {
|
||||
return groupList[item.value];
|
||||
});
|
||||
|
||||
const list = _.sortBy(res.items, 'size');
|
||||
|
||||
const defaultSpec =
|
||||
_.find(
|
||||
list,
|
||||
(item: CatalogSpec) => item.mode === modeDataList[0]?.value
|
||||
) || {};
|
||||
|
||||
selectSpecRef.current = defaultSpec;
|
||||
|
||||
setModeList(modeDataList);
|
||||
setSourceList(sources);
|
||||
initFormDataBySource({
|
||||
...defaultSpec,
|
||||
cluster_id: clusterId
|
||||
});
|
||||
|
||||
const name = _.toLower(current.name).replace(/\s/g, '-') || '';
|
||||
form.current.setFieldValue('name', name);
|
||||
|
||||
if (defaultSpec.backend === backendOptionsMap.llamaBox) {
|
||||
setIsGGUF(true);
|
||||
} else {
|
||||
setIsGGUF(false);
|
||||
}
|
||||
const allValues = generateSubmitData({
|
||||
...defaultSpec,
|
||||
categories: _.get(current, 'categories.0', null),
|
||||
cluster_id: clusterId,
|
||||
name
|
||||
});
|
||||
|
||||
// If no avaliable gpus for the model, show warning message
|
||||
if (!res.items.length) {
|
||||
noCompatibleGPUsRef.current = true;
|
||||
setWarningStatus({
|
||||
show: true,
|
||||
type: 'warning',
|
||||
message: intl.formatMessage({ id: 'models.catalog.nogpus.tips' })
|
||||
});
|
||||
return;
|
||||
}
|
||||
noCompatibleGPUsRef.current = false;
|
||||
handleCheckCompatibility(allValues);
|
||||
} catch (error) {
|
||||
// ignore
|
||||
}
|
||||
};
|
||||
|
||||
const handleOnModeChange = (val: string) => {
|
||||
const data = getModelSpec({
|
||||
mode: val,
|
||||
backend: form.current.getFieldValue('backend'),
|
||||
size: 0,
|
||||
quantization: ''
|
||||
});
|
||||
|
||||
console.log('mode change data:', data);
|
||||
|
||||
form.current.setFieldsValue({
|
||||
...data
|
||||
});
|
||||
handleCheckFormData();
|
||||
};
|
||||
|
||||
const handleOnClusterChange = async (clusterId: number) => {
|
||||
await fetchSpecData(clusterId);
|
||||
};
|
||||
|
||||
const handleOk = async (values: FormData) => {
|
||||
const data = {
|
||||
..._.omit(selectSpecRef.current, ['name']),
|
||||
...values
|
||||
};
|
||||
onOk(data);
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
onCancel?.();
|
||||
axiosToken.current?.cancel?.();
|
||||
};
|
||||
|
||||
const showExtraButton = useMemo(() => {
|
||||
return warningStatus.show && warningStatus.type !== 'success';
|
||||
}, [warningStatus.show, warningStatus.type]);
|
||||
|
||||
useEffect(() => {
|
||||
getClusterList();
|
||||
getWorkerList();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setTimeout(() => {
|
||||
const clusterId = initClusterId();
|
||||
fetchSpecData(clusterId);
|
||||
form.current?.getGPUOptionList?.({
|
||||
clusterId: clusterId
|
||||
});
|
||||
form.current?.getBackendOptions?.({
|
||||
cluster_id: clusterId
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
return () => {
|
||||
axiosToken.current?.cancel?.();
|
||||
cancelEvaluate();
|
||||
setWarningStatus({
|
||||
show: false,
|
||||
title: '',
|
||||
message: []
|
||||
});
|
||||
};
|
||||
}, [open, current]);
|
||||
|
||||
return (
|
||||
<GSDrawer
|
||||
title={title}
|
||||
open={open}
|
||||
onClose={handleCancel}
|
||||
destroyOnHidden={true}
|
||||
closeIcon={false}
|
||||
mask={{
|
||||
closable: false
|
||||
}}
|
||||
keyboard={false}
|
||||
styles={{
|
||||
wrapper: { width: width }
|
||||
}}
|
||||
footer={false}
|
||||
>
|
||||
<CatalogFormContext.Provider
|
||||
value={{
|
||||
sizeOptions: [],
|
||||
quantizationOptions: [],
|
||||
modeList: modeList,
|
||||
onModeChange: handleOnModeChange
|
||||
}}
|
||||
>
|
||||
<FormWrapper>
|
||||
<ColumnWrapper
|
||||
styles={{
|
||||
container: {
|
||||
paddingBlock: 0
|
||||
}
|
||||
}}
|
||||
footer={
|
||||
<>
|
||||
<CompatibilityAlert
|
||||
showClose={true}
|
||||
onClose={() => {
|
||||
setWarningStatus({
|
||||
show: false,
|
||||
message: ''
|
||||
});
|
||||
}}
|
||||
warningStatus={warningStatus}
|
||||
contentStyle={{ paddingInline: '0 6px' }}
|
||||
></CompatibilityAlert>
|
||||
<ModalFooter
|
||||
onCancel={handleCancel}
|
||||
onOk={handleSumit}
|
||||
showOkBtn={!showExtraButton}
|
||||
extra={
|
||||
showExtraButton && (
|
||||
<Button type="primary" onClick={handleSubmitAnyway}>
|
||||
{intl.formatMessage({
|
||||
id: 'models.form.submit.anyway'
|
||||
})}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
style={{
|
||||
padding: '16px 24px 8px',
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end'
|
||||
}}
|
||||
></ModalFooter>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<>
|
||||
<DataForm
|
||||
fields={[]}
|
||||
source={source}
|
||||
action={action}
|
||||
onOk={handleOk}
|
||||
ref={form}
|
||||
isGGUF={isGGUF}
|
||||
formKey={DeployFormKeyMap.CATALOG}
|
||||
sourceDisable={false}
|
||||
sourceList={sourceList}
|
||||
clusterList={clusterList}
|
||||
onClusterChange={handleOnClusterChange}
|
||||
onBackendChange={handleBackendChange}
|
||||
onSourceChange={handleSourceChange}
|
||||
onValuesChange={onValuesChange}
|
||||
clearCacheFormValues={clearCacheFormValues}
|
||||
></DataForm>
|
||||
</>
|
||||
</ColumnWrapper>
|
||||
</FormWrapper>
|
||||
</CatalogFormContext.Provider>
|
||||
</GSDrawer>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddModal;
|
||||
@@ -0,0 +1,682 @@
|
||||
import { getRequestId } from '@/atoms/models';
|
||||
import ModalFooter from '@/components/modal-footer';
|
||||
import GSDrawer from '@/components/scroller-modal/gs-drawer';
|
||||
import { PageActionType } from '@/config/types';
|
||||
import useDeferredRequest from '@/hooks/use-deferred-request';
|
||||
import { ClusterStatusValueMap } from '@/pages/cluster-management/config';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { Button } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import { FC, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import ColumnWrapper from '../../../_components/column-wrapper';
|
||||
import {
|
||||
defaultFormValues,
|
||||
DeployFormKeyMap,
|
||||
modelSourceMap
|
||||
} from '../../config';
|
||||
import { FormData, SourceType } from '../../config/types';
|
||||
import { backendOptionsMap } from '../../constants/backend-parameters';
|
||||
import DataForm from '../../forms';
|
||||
import {
|
||||
MessageStatus,
|
||||
useCheckCompatibility,
|
||||
useSelectModel,
|
||||
WarningStausOptions
|
||||
} from '../../hooks';
|
||||
import useCheckBackend from '../../hooks/use-check-backend';
|
||||
import CompatibilityAlert from '../compatible-alert';
|
||||
import HFModelFile from '../model-source/hf-model-file';
|
||||
import ModelCard from '../model-source/model-card';
|
||||
import SearchModel from '../model-source/search-model';
|
||||
import Separator from '../separator';
|
||||
import TitleWrapper from '../title-wrapper';
|
||||
|
||||
const pickFieldsFromSpec = ['backend_version', 'backend_parameters', 'env'];
|
||||
const dropFieldsFromForm = [
|
||||
'name',
|
||||
'huggingface_filename',
|
||||
'model_scope_file_path',
|
||||
'model_scope_model_id',
|
||||
'huggingface_repo_id',
|
||||
'backend'
|
||||
];
|
||||
const resetFields = ['worker_selector', 'env'];
|
||||
|
||||
const ModalFooterStyle = {
|
||||
padding: '16px 24px 8px',
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end'
|
||||
};
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
const ColWrapper = styled.div`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
max-width: 33.33%;
|
||||
`;
|
||||
|
||||
const FormWrapper = styled.div`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
maxwidth: 100%;
|
||||
`;
|
||||
|
||||
type AddModalProps = {
|
||||
title: string;
|
||||
hasLinuxWorker?: boolean;
|
||||
action: PageActionType;
|
||||
open: boolean;
|
||||
source: SourceType;
|
||||
isGGUF?: boolean;
|
||||
width?: string | number;
|
||||
initialValues?: any;
|
||||
deploymentType?: 'modelList' | 'modelFiles';
|
||||
clusterList: Global.BaseOption<
|
||||
number,
|
||||
{ provider: string; state: string | number; is_default: boolean }
|
||||
>[];
|
||||
onOk: (values: FormData) => void;
|
||||
onCancel: () => void;
|
||||
};
|
||||
|
||||
type EvaluateProccessType = 'model' | 'file' | 'form';
|
||||
|
||||
const EvaluateProccess: Record<string, EvaluateProccessType> = {
|
||||
model: 'model',
|
||||
file: 'file',
|
||||
form: 'form'
|
||||
};
|
||||
|
||||
const AddModal: FC<AddModalProps> = (props) => {
|
||||
const {
|
||||
title,
|
||||
open,
|
||||
onOk,
|
||||
onCancel,
|
||||
hasLinuxWorker,
|
||||
source,
|
||||
action,
|
||||
width = 600,
|
||||
deploymentType = 'modelList',
|
||||
initialValues,
|
||||
clusterList
|
||||
} = props || {};
|
||||
const SEARCH_SOURCE = [
|
||||
modelSourceMap.huggingface_value,
|
||||
modelSourceMap.modelscope_value
|
||||
];
|
||||
|
||||
const { checkOnlyAscendNPU } = useCheckBackend();
|
||||
const {
|
||||
setWarningStatus,
|
||||
handleBackendChangeBefore,
|
||||
cancelEvaluate,
|
||||
unlockWarningStatus,
|
||||
handleOnValuesChange: handleOnValuesChangeBefore,
|
||||
clearCacheFormValues,
|
||||
warningStatus,
|
||||
submitAnyway
|
||||
} = useCheckCompatibility();
|
||||
const { onSelectModel } = useSelectModel({ gpuOptions: [] });
|
||||
const form = useRef<any>({});
|
||||
const intl = useIntl();
|
||||
const [selectedModel, setSelectedModel] = useState<any>({});
|
||||
const [collapsed, setCollapsed] = useState<boolean>(false);
|
||||
const [isGGUF, setIsGGUF] = useState<boolean>(false);
|
||||
const modelFileRef = useRef<any>(null);
|
||||
const evaluateStateRef = useRef<{
|
||||
state: EvaluateProccessType;
|
||||
requestModelId: number;
|
||||
}>({
|
||||
state: 'form',
|
||||
requestModelId: 0
|
||||
});
|
||||
const requestModelIdRef = useRef<number>(0);
|
||||
const currentSelectedModel = useRef<any>({});
|
||||
const flatBackendOptionsRef = useRef<any[]>([]);
|
||||
|
||||
const { run: fetchModelFiles } = useDeferredRequest(
|
||||
() => modelFileRef.current?.fetchModelFiles?.(),
|
||||
100
|
||||
);
|
||||
|
||||
const updateSelectedModel = (model: any) => {
|
||||
currentSelectedModel.current = model;
|
||||
setSelectedModel(model);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the request model id to distinguish
|
||||
* the evaluate request.
|
||||
*/
|
||||
const updateRequestModelId = () => {
|
||||
requestModelIdRef.current += 1;
|
||||
return requestModelIdRef.current;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param state target to distinguish the evaluate state, current evaluate state
|
||||
* can be 'model', 'file' or 'form'.
|
||||
*/
|
||||
const setEvaluteState = (state: {
|
||||
state: EvaluateProccessType;
|
||||
requestModelId: number;
|
||||
}) => {
|
||||
evaluateStateRef.current = state;
|
||||
};
|
||||
|
||||
const handleOnValuesChange = (data: {
|
||||
changedValues: any;
|
||||
allValues: any;
|
||||
source: SourceType;
|
||||
}) => {
|
||||
setEvaluteState({
|
||||
state: EvaluateProccess.form,
|
||||
requestModelId: updateRequestModelId()
|
||||
});
|
||||
handleOnValuesChangeBefore(data);
|
||||
};
|
||||
|
||||
const getDefaultSpec = (item: any) => {
|
||||
const defaultSpec = _.pick(
|
||||
item.evaluateResult?.default_spec,
|
||||
pickFieldsFromSpec
|
||||
);
|
||||
|
||||
return defaultSpec;
|
||||
};
|
||||
|
||||
const getCategory = (item: any) => {
|
||||
const categories = item.evaluateResult?.default_spec?.categories || [];
|
||||
if (Array.isArray(categories)) {
|
||||
return categories?.[0] || null;
|
||||
}
|
||||
return categories || null;
|
||||
};
|
||||
|
||||
const { run: onClickModel } = useDeferredRequest(async () => {
|
||||
const allValues = form.current?.form?.getFieldsValue?.();
|
||||
|
||||
handleOnValuesChangeBefore({
|
||||
changedValues: {},
|
||||
allValues: allValues,
|
||||
source: props.source
|
||||
});
|
||||
}, 100);
|
||||
|
||||
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']),
|
||||
huggingface_filename: item.fakeName,
|
||||
model_scope_file_path: 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;
|
||||
}
|
||||
|
||||
const modelInfo = onSelectModel(selectedModel, {
|
||||
source: props.source,
|
||||
defaultBackend: form.current?.getFieldValue?.('backend'),
|
||||
flatBackendOptions: flatBackendOptionsRef.current
|
||||
});
|
||||
|
||||
form.current?.setFieldsValue?.({
|
||||
..._.omit(modelInfo, ['name']),
|
||||
huggingface_filename: item.fakeName,
|
||||
model_scope_file_path: item.fakeName,
|
||||
backend_parameters: [],
|
||||
backend_version: null,
|
||||
backend: modelInfo.backend,
|
||||
env: {
|
||||
...modelInfo.env
|
||||
},
|
||||
categories: getCategory(item)
|
||||
});
|
||||
|
||||
// evaluate the form data when select a model file
|
||||
// TODO: reset backend related fields when select a GGUF file
|
||||
if (item.fakeName) {
|
||||
onSelectFile(item, modelInfo, manual);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancelFiles = () => {
|
||||
cancelEvaluate();
|
||||
modelFileRef.current?.cancelRequest();
|
||||
};
|
||||
|
||||
const generateNameValue = (
|
||||
item: any,
|
||||
modelName: string,
|
||||
manual?: boolean
|
||||
) => {
|
||||
if (item.name === currentSelectedModel.current.name) {
|
||||
return manual
|
||||
? modelName
|
||||
: form.current?.getFieldValue?.('name') || modelName;
|
||||
}
|
||||
return modelName;
|
||||
};
|
||||
|
||||
const currentModelDuringEvaluate = (item: any) => {
|
||||
return (
|
||||
evaluateStateRef.current.state === EvaluateProccess.form &&
|
||||
item.name === currentSelectedModel.current.name
|
||||
);
|
||||
};
|
||||
|
||||
const handleOnSelectModel = async (item: any, manual?: boolean) => {
|
||||
// If the item is empty or the same as the selected model, do nothing
|
||||
|
||||
handleCancelFiles();
|
||||
if (
|
||||
_.isEmpty(item) ||
|
||||
(item.isGGUF === selectedModel.isGGUF && item.name === selectedModel.name) // --- because sometimes has the same model name with different isGGUF value
|
||||
) {
|
||||
return;
|
||||
}
|
||||
setIsGGUF(item.isGGUF);
|
||||
clearCacheFormValues();
|
||||
unlockWarningStatus();
|
||||
setEvaluteState({
|
||||
state: EvaluateProccess.model,
|
||||
requestModelId: updateRequestModelId()
|
||||
});
|
||||
|
||||
// TODO
|
||||
form.current?.resetFields(resetFields);
|
||||
const modelInfo = onSelectModel(item, {
|
||||
source: props.source,
|
||||
flatBackendOptions: flatBackendOptionsRef.current
|
||||
});
|
||||
form.current?.setFieldsValue?.({
|
||||
...defaultFormValues,
|
||||
...modelInfo,
|
||||
env: {
|
||||
...modelInfo.env
|
||||
},
|
||||
name: generateNameValue(item, modelInfo.name, manual),
|
||||
categories: getCategory(item)
|
||||
});
|
||||
|
||||
updateSelectedModel(item);
|
||||
|
||||
let warningStatus: MessageStatus = {
|
||||
show: true,
|
||||
title: '',
|
||||
type: 'transition',
|
||||
message: intl.formatMessage({ id: 'models.form.evaluating' })
|
||||
};
|
||||
|
||||
if (item.isGGUF) {
|
||||
fetchModelFiles();
|
||||
}
|
||||
setWarningStatus(warningStatus, { override: true });
|
||||
};
|
||||
|
||||
const handleOnSelectModelAfterEvaluate = (item: any, manual?: boolean) => {
|
||||
if (currentModelDuringEvaluate(item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (manual) {
|
||||
form.current?.resetFields(resetFields);
|
||||
}
|
||||
// If the item is empty
|
||||
setIsGGUF(item.isGGUF);
|
||||
updateSelectedModel(item);
|
||||
setEvaluteState({
|
||||
state: EvaluateProccess.model,
|
||||
requestModelId: updateRequestModelId()
|
||||
});
|
||||
handleCancelFiles();
|
||||
const modelInfo = onSelectModel(item, {
|
||||
source: props.source,
|
||||
flatBackendOptions: flatBackendOptionsRef.current
|
||||
});
|
||||
|
||||
if (
|
||||
evaluateStateRef.current.state === EvaluateProccess.model &&
|
||||
item.evaluated
|
||||
) {
|
||||
const defaultSpec = getDefaultSpec(item);
|
||||
const newFormValues = {
|
||||
...(manual
|
||||
? { ...defaultFormValues }
|
||||
: _.omit(form.current?.form?.getFieldsValue?.(), [
|
||||
...dropFieldsFromForm
|
||||
])),
|
||||
...defaultSpec,
|
||||
...modelInfo,
|
||||
env: {
|
||||
...modelInfo.env,
|
||||
...defaultSpec.env
|
||||
},
|
||||
name: generateNameValue(item, modelInfo.name, manual),
|
||||
categories: getCategory(item)
|
||||
};
|
||||
|
||||
form.current?.form?.setFieldsValue?.(newFormValues);
|
||||
|
||||
onClickModel();
|
||||
}
|
||||
};
|
||||
|
||||
const handleOnOk = async (allValues: FormData) => {
|
||||
onOk(allValues);
|
||||
};
|
||||
|
||||
const handleSubmitAnyway = async () => {
|
||||
submitAnyway.current = true;
|
||||
form.current?.submit?.();
|
||||
};
|
||||
|
||||
const handleSumit = () => {
|
||||
form.current?.submit?.();
|
||||
};
|
||||
|
||||
const handleSetIsGGUF = async (flag: boolean) => {
|
||||
setIsGGUF(flag);
|
||||
};
|
||||
|
||||
const handleBackendChange = async (backend: string) => {
|
||||
const data = form.current.form.getFieldsValue?.();
|
||||
const res = handleBackendChangeBefore(data);
|
||||
if (res.show) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: confirm gguf change backend behavior
|
||||
handleOnValuesChange?.({
|
||||
changedValues: {},
|
||||
allValues: data,
|
||||
source: props.source
|
||||
});
|
||||
};
|
||||
|
||||
const onValuesChange = async (changedValues: any, allValues: any) => {
|
||||
handleOnValuesChange?.({
|
||||
changedValues,
|
||||
allValues,
|
||||
source: props.source
|
||||
});
|
||||
};
|
||||
|
||||
const handleCancel = useMemoizedFn(() => {
|
||||
onCancel?.();
|
||||
});
|
||||
|
||||
const initClusterId = () => {
|
||||
if (initialValues?.cluster_id) {
|
||||
return initialValues.cluster_id;
|
||||
}
|
||||
// Find default cluster
|
||||
const defaultCluster = clusterList?.find((item) => item.is_default);
|
||||
if (defaultCluster) {
|
||||
return defaultCluster.value;
|
||||
}
|
||||
|
||||
const cluster_id =
|
||||
clusterList?.find((item) => item.state === ClusterStatusValueMap.Ready)
|
||||
?.value || clusterList?.[0]?.value;
|
||||
|
||||
return cluster_id;
|
||||
};
|
||||
|
||||
const handleOnOpen = async () => {
|
||||
const [backendOptions, gpuOptions] = await Promise.all([
|
||||
form.current?.getBackendOptions?.({
|
||||
cluster_id: initClusterId()
|
||||
}),
|
||||
form.current?.getGPUOptionList?.({
|
||||
clusterId: initClusterId()
|
||||
})
|
||||
]);
|
||||
|
||||
flatBackendOptionsRef.current = backendOptions;
|
||||
|
||||
if (props.deploymentType === 'modelFiles') {
|
||||
form.current?.form?.setFieldsValue({
|
||||
...props.initialValues
|
||||
});
|
||||
handleOnValuesChange?.({
|
||||
changedValues: {},
|
||||
allValues: form.current?.form?.getFieldsValue(),
|
||||
source: source
|
||||
});
|
||||
} else {
|
||||
let backend = checkOnlyAscendNPU(gpuOptions)
|
||||
? backendOptionsMap.ascendMindie
|
||||
: backendOptionsMap.vllm;
|
||||
|
||||
const currentDefaultBackend = backendOptions?.find(
|
||||
(item: {
|
||||
value: string;
|
||||
label: string;
|
||||
default_backend_param: string[];
|
||||
default_version: string;
|
||||
versions: { label: string; value: string }[];
|
||||
}) => item.value === backend
|
||||
);
|
||||
|
||||
form.current?.setFieldsValue?.({
|
||||
backend,
|
||||
env: {
|
||||
...currentDefaultBackend?.default_env
|
||||
},
|
||||
default_version: currentDefaultBackend?.default_version,
|
||||
backend_parameters: currentDefaultBackend?.default_backend_param || [],
|
||||
cluster_id: initClusterId()
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const showExtraButton = useMemo(() => {
|
||||
return warningStatus.show && warningStatus.type !== 'success';
|
||||
}, [warningStatus.show, warningStatus.type]);
|
||||
|
||||
// This is only a placeholder for querying the model or file during the transition period.
|
||||
const displayEvaluateStatus = (
|
||||
params: MessageStatus,
|
||||
options?: WarningStausOptions
|
||||
) => {
|
||||
setWarningStatus(
|
||||
{
|
||||
show: params.show,
|
||||
title: '',
|
||||
type: 'transition',
|
||||
message: intl.formatMessage({ id: 'models.form.evaluating' })
|
||||
},
|
||||
options
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
handleOnOpen();
|
||||
} else {
|
||||
cancelEvaluate();
|
||||
clearCacheFormValues();
|
||||
}
|
||||
return () => {
|
||||
setSelectedModel({});
|
||||
setWarningStatus({
|
||||
show: false,
|
||||
title: '',
|
||||
message: []
|
||||
});
|
||||
};
|
||||
}, [open, clusterList, initialValues?.cluster_id]);
|
||||
|
||||
return (
|
||||
<GSDrawer
|
||||
title={title}
|
||||
open={open}
|
||||
onClose={handleCancel}
|
||||
destroyOnHidden={true}
|
||||
closeIcon={false}
|
||||
mask={{
|
||||
closable: false
|
||||
}}
|
||||
keyboard={false}
|
||||
styles={{
|
||||
wrapper: { width: width }
|
||||
}}
|
||||
footer={false}
|
||||
>
|
||||
<Container>
|
||||
{SEARCH_SOURCE.includes(props.source) &&
|
||||
deploymentType === 'modelList' && (
|
||||
<>
|
||||
<ColWrapper>
|
||||
<SearchModel
|
||||
hasLinuxWorker={hasLinuxWorker}
|
||||
modelSource={props.source}
|
||||
onSelectModel={handleOnSelectModel}
|
||||
onSelectModelAfterEvaluate={handleOnSelectModelAfterEvaluate}
|
||||
clusterId={
|
||||
form.current?.getFieldValue?.('cluster_id') ||
|
||||
initClusterId()
|
||||
}
|
||||
displayEvaluateStatus={displayEvaluateStatus}
|
||||
gpuOptions={[]}
|
||||
></SearchModel>
|
||||
<Separator></Separator>
|
||||
</ColWrapper>
|
||||
<ColWrapper>
|
||||
<ColumnWrapper styles={{ container: { padding: 0 } }}>
|
||||
<ModelCard
|
||||
selectedModel={selectedModel}
|
||||
onCollapse={setCollapsed}
|
||||
collapsed={collapsed}
|
||||
modelSource={props.source}
|
||||
isGGUF={isGGUF}
|
||||
setIsGGUF={handleSetIsGGUF}
|
||||
></ModelCard>
|
||||
{isGGUF && (
|
||||
<HFModelFile
|
||||
ref={modelFileRef}
|
||||
selectedModel={selectedModel}
|
||||
modelSource={props.source}
|
||||
onSelectFile={handleSelectModelFile}
|
||||
collapsed={collapsed}
|
||||
></HFModelFile>
|
||||
)}
|
||||
</ColumnWrapper>
|
||||
<Separator></Separator>
|
||||
</ColWrapper>
|
||||
</>
|
||||
)}
|
||||
<FormWrapper>
|
||||
<ColumnWrapper
|
||||
styles={{
|
||||
container: { paddingBlock: 0 }
|
||||
}}
|
||||
footer={
|
||||
<>
|
||||
<CompatibilityAlert
|
||||
showClose={true}
|
||||
onClose={() => {
|
||||
setWarningStatus({
|
||||
show: false,
|
||||
message: ''
|
||||
});
|
||||
}}
|
||||
warningStatus={warningStatus}
|
||||
contentStyle={{ paddingInline: '0 6px' }}
|
||||
></CompatibilityAlert>
|
||||
<ModalFooter
|
||||
onCancel={handleCancel}
|
||||
onOk={handleSumit}
|
||||
showOkBtn={!showExtraButton}
|
||||
extra={
|
||||
showExtraButton && (
|
||||
<Button type="primary" onClick={handleSubmitAnyway}>
|
||||
{intl.formatMessage({
|
||||
id: 'models.form.submit.anyway'
|
||||
})}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
style={ModalFooterStyle}
|
||||
></ModalFooter>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<>
|
||||
{SEARCH_SOURCE.includes(source) &&
|
||||
deploymentType === 'modelList' && (
|
||||
<TitleWrapper>
|
||||
{intl.formatMessage({ id: 'models.form.configurations' })}
|
||||
</TitleWrapper>
|
||||
)}
|
||||
<DataForm
|
||||
formKey={DeployFormKeyMap.DEPLOYMENT}
|
||||
initialValues={initialValues}
|
||||
source={source}
|
||||
action={action}
|
||||
clusterList={clusterList}
|
||||
onOk={handleOnOk}
|
||||
ref={form}
|
||||
isGGUF={isGGUF}
|
||||
onBackendChange={handleBackendChange}
|
||||
onValuesChange={onValuesChange}
|
||||
clearCacheFormValues={clearCacheFormValues}
|
||||
></DataForm>
|
||||
</>
|
||||
</ColumnWrapper>
|
||||
</FormWrapper>
|
||||
</Container>
|
||||
</GSDrawer>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddModal;
|
||||
@@ -0,0 +1,293 @@
|
||||
import ModalFooter from '@/components/modal-footer';
|
||||
import GSDrawer from '@/components/scroller-modal/gs-drawer';
|
||||
import { PageAction } from '@/config';
|
||||
import { PageActionType } from '@/config/types';
|
||||
import ColumnWrapper from '@/pages/_components/column-wrapper';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import _ from 'lodash';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import {
|
||||
DeployFormKeyMap,
|
||||
DO_NOT_NOTIFY_RECREATE,
|
||||
ScheduleValueMap
|
||||
} from '../../config';
|
||||
import { FormData } from '../../config/types';
|
||||
import { backendOptionsMap } from '../../constants/backend-parameters';
|
||||
import DataForm from '../../forms';
|
||||
import { useCheckCompatibility } from '../../hooks';
|
||||
import { generateGPUSelector } from '../../utils';
|
||||
import CompatibilityAlert from '../compatible-alert';
|
||||
|
||||
type AddModalProps = {
|
||||
title: string;
|
||||
action: PageActionType;
|
||||
open: boolean;
|
||||
currentData: {
|
||||
data: FormData;
|
||||
isGGUF: boolean;
|
||||
realAction?: PageActionType;
|
||||
};
|
||||
clusterList: Global.BaseOption<
|
||||
number,
|
||||
{ provider: string; state: string | number }
|
||||
>[];
|
||||
onOk: (values: FormData) => void;
|
||||
onCancel: () => void;
|
||||
};
|
||||
|
||||
const ModalFooterStyle = {
|
||||
padding: '16px 24px 8px',
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end'
|
||||
};
|
||||
|
||||
const UpdateModal: React.FC<AddModalProps> = (props) => {
|
||||
const {
|
||||
title,
|
||||
action,
|
||||
open,
|
||||
onOk,
|
||||
onCancel,
|
||||
clusterList,
|
||||
currentData: { isGGUF, data: formData, realAction }
|
||||
} = props || {};
|
||||
const intl = useIntl();
|
||||
const {
|
||||
setWarningStatus,
|
||||
handleBackendChangeBefore,
|
||||
checkTokenRef,
|
||||
warningStatus
|
||||
} = useCheckCompatibility();
|
||||
|
||||
const formRef = useRef<any>(null);
|
||||
const submitAnyway = useRef<boolean>(false);
|
||||
const originFormData = useRef<any>(null);
|
||||
|
||||
const setOriginalFormData = () => {
|
||||
if (!originFormData.current) {
|
||||
originFormData.current = _.cloneDeep(formData);
|
||||
if (!originFormData.current.extended_kv_cache?.enabled) {
|
||||
originFormData.current.extended_kv_cache = {
|
||||
enabled: false
|
||||
};
|
||||
}
|
||||
|
||||
if (!originFormData.current.speculative_config?.enabled) {
|
||||
originFormData.current.speculative_config = {
|
||||
enabled: false
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const customizer = (val1: any, val2: any) => {
|
||||
if (
|
||||
(val1 === null && val2 === '') ||
|
||||
(val1 === '' && val2 === null) ||
|
||||
(_.isEmpty(val1) && val2 === null) ||
|
||||
(_.isEmpty(val2) && val1 === null)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
// this function is used compare form data changes in updating model, and show warning if needed
|
||||
const handleOnValuesChange = _.debounce((data: any) => {
|
||||
const formdata = formRef.current?.getFieldsValue?.();
|
||||
console.log('handleOnValuesChange:', formdata);
|
||||
|
||||
let alldata = {};
|
||||
if (formdata.scheduleType === ScheduleValueMap.Manual) {
|
||||
alldata = {
|
||||
..._.omit(formdata, ['worker_selector']),
|
||||
env: formdata.env || originFormData.current?.env || null,
|
||||
gpu_selector: formdata.gpu_selector
|
||||
};
|
||||
} else {
|
||||
alldata = {
|
||||
..._.omit(formdata, ['gpu_selector']),
|
||||
env: formdata.env || originFormData.current?.env || null,
|
||||
worker_selector:
|
||||
formdata.worker_selector ||
|
||||
originFormData.current?.worker_selector ||
|
||||
null
|
||||
};
|
||||
}
|
||||
|
||||
const originalData = _.pick(originFormData.current, Object.keys(alldata));
|
||||
|
||||
const isEqual = _.isEqualWith(
|
||||
_.omit(alldata, DO_NOT_NOTIFY_RECREATE),
|
||||
_.omit(originalData, DO_NOT_NOTIFY_RECREATE),
|
||||
customizer
|
||||
);
|
||||
|
||||
if (isEqual) {
|
||||
setWarningStatus({
|
||||
show: false,
|
||||
message: ''
|
||||
});
|
||||
} else {
|
||||
setWarningStatus({
|
||||
show: true,
|
||||
isDefault: true,
|
||||
message: intl.formatMessage({
|
||||
id: 'models.form.update.tips'
|
||||
})
|
||||
});
|
||||
}
|
||||
}, 100);
|
||||
|
||||
const handleBackendChange = (backend: string) => {
|
||||
const data = formRef.current?.getFieldsValue?.();
|
||||
const res = handleBackendChangeBefore(data);
|
||||
if (res.show) {
|
||||
return;
|
||||
}
|
||||
handleOnValuesChange?.({
|
||||
changedValues: {},
|
||||
allValues: data,
|
||||
source: data.source
|
||||
});
|
||||
};
|
||||
|
||||
const handleAsyncBackendChange = (backend: string) => {
|
||||
setTimeout(() => {
|
||||
handleBackendChange(backend);
|
||||
}, 100);
|
||||
};
|
||||
|
||||
const handleSumit = () => {
|
||||
formRef.current?.submit();
|
||||
};
|
||||
|
||||
const handleSubmitAnyway = async () => {
|
||||
submitAnyway.current = true;
|
||||
formRef.current?.submit?.();
|
||||
};
|
||||
|
||||
const handleOk = async (formdata: FormData) => {
|
||||
let submitData = {} as FormData;
|
||||
const isVoxBox = [backendOptionsMap.voxBox].includes(formdata.backend);
|
||||
|
||||
submitData = {
|
||||
..._.omit(formdata, ['scheduleType']),
|
||||
worker_selector:
|
||||
formdata.scheduleType === ScheduleValueMap.Manual
|
||||
? null
|
||||
: formdata.worker_selector,
|
||||
...(isVoxBox
|
||||
? {
|
||||
distributed_inference_across_workers: false,
|
||||
cpu_offloading: false
|
||||
}
|
||||
: {})
|
||||
};
|
||||
onOk(submitData);
|
||||
};
|
||||
|
||||
const handleManulOnValuesChange = (changedValues: any, allValues: any) => {
|
||||
handleOnValuesChange({
|
||||
changedValues,
|
||||
allValues,
|
||||
source: formData?.source as string
|
||||
});
|
||||
};
|
||||
|
||||
const handleOnClose = () => {
|
||||
onCancel?.();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const initGPUSelector = async () => {
|
||||
const gpuOptions = await formRef.current?.getGPUOptionList({
|
||||
clusterId: formData.cluster_id
|
||||
});
|
||||
const gpuSelector = generateGPUSelector(formData, gpuOptions);
|
||||
formRef.current?.setFieldsValue(gpuSelector);
|
||||
};
|
||||
|
||||
if (open && formData) {
|
||||
setTimeout(() => {
|
||||
setOriginalFormData();
|
||||
initGPUSelector();
|
||||
formRef.current?.getBackendOptions?.({
|
||||
cluster_id: formData.cluster_id
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
if (!open) {
|
||||
checkTokenRef.current?.cancel?.();
|
||||
originFormData.current = null;
|
||||
setWarningStatus({
|
||||
show: false,
|
||||
message: ''
|
||||
});
|
||||
}
|
||||
}, [open, formData]);
|
||||
|
||||
return (
|
||||
<GSDrawer
|
||||
title={title}
|
||||
open={open}
|
||||
onClose={handleOnClose}
|
||||
destroyOnHidden={true}
|
||||
closeIcon={true}
|
||||
mask={{
|
||||
closable: false
|
||||
}}
|
||||
keyboard={false}
|
||||
styles={{
|
||||
wrapper: { width: 600 }
|
||||
}}
|
||||
footer={false}
|
||||
>
|
||||
<ColumnWrapper
|
||||
styles={{
|
||||
container: {
|
||||
paddingBlock: 0
|
||||
}
|
||||
}}
|
||||
footer={
|
||||
<>
|
||||
{realAction === PageAction.EDIT && (
|
||||
<CompatibilityAlert
|
||||
showClose={false}
|
||||
onClose={() => {
|
||||
setWarningStatus({
|
||||
show: false,
|
||||
message: ''
|
||||
});
|
||||
}}
|
||||
warningStatus={warningStatus}
|
||||
contentStyle={{ paddingInline: 0 }}
|
||||
></CompatibilityAlert>
|
||||
)}
|
||||
<ModalFooter
|
||||
style={ModalFooterStyle}
|
||||
onCancel={onCancel}
|
||||
onOk={handleSumit}
|
||||
></ModalFooter>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<DataForm
|
||||
formKey={DeployFormKeyMap.DEPLOYMENT}
|
||||
initialValues={formData}
|
||||
source={formData.source || ''}
|
||||
action={action}
|
||||
realAction={realAction}
|
||||
clusterList={clusterList}
|
||||
onOk={handleOk}
|
||||
ref={formRef}
|
||||
isGGUF={isGGUF}
|
||||
onBackendChange={handleAsyncBackendChange}
|
||||
onValuesChange={handleManulOnValuesChange}
|
||||
></DataForm>
|
||||
</ColumnWrapper>
|
||||
</GSDrawer>
|
||||
);
|
||||
};
|
||||
|
||||
export default UpdateModal;
|
||||
Reference in New Issue
Block a user