fix: gpu selector by cluster
This commit is contained in:
@@ -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`, {
|
||||
methos: 'GET',
|
||||
method: 'GET',
|
||||
params
|
||||
});
|
||||
}
|
||||
@@ -172,7 +174,7 @@ export async function queryModelScopeModels(
|
||||
config?: any
|
||||
) {
|
||||
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) => {
|
||||
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 GPUCard from './gpu-card';
|
||||
import Performance from './performance';
|
||||
import Scaling from './scaling';
|
||||
|
||||
interface AdvanceConfigProps {
|
||||
isGGUF: boolean;
|
||||
@@ -460,19 +459,19 @@ const AdvanceConfig: React.FC<AdvanceConfigProps> = (props) => {
|
||||
forceRender: true,
|
||||
children: <Performance></Performance>
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
label: (
|
||||
<span
|
||||
style={{ fontWeight: 'var(--font-weight-medium)' }}
|
||||
className="font-size-14"
|
||||
>
|
||||
Scaling
|
||||
</span>
|
||||
),
|
||||
forceRender: true,
|
||||
children: <Scaling></Scaling>
|
||||
},
|
||||
// {
|
||||
// key: '3',
|
||||
// label: (
|
||||
// <span
|
||||
// style={{ fontWeight: 'var(--font-weight-medium)' }}
|
||||
// className="font-size-14"
|
||||
// >
|
||||
// Scaling
|
||||
// </span>
|
||||
// ),
|
||||
// forceRender: true,
|
||||
// children: <Scaling></Scaling>
|
||||
// },
|
||||
{
|
||||
key: '1',
|
||||
label: (
|
||||
|
||||
@@ -6,17 +6,13 @@ import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import React, { forwardRef, useImperativeHandle } from 'react';
|
||||
import {
|
||||
backendOptionsMap,
|
||||
excludeFields,
|
||||
modelSourceMap,
|
||||
sourceOptions
|
||||
} from '../config';
|
||||
import { backendOptionsMap, excludeFields, sourceOptions } from '../config';
|
||||
import { FormInnerContext } from '../config/form-context';
|
||||
import { FormData, SourceType } from '../config/types';
|
||||
import CatalogFrom from '../forms/catalog';
|
||||
import HuggingFaceForm from '../forms/hugging-face';
|
||||
import LocalPathForm from '../forms/local-path';
|
||||
import { useGenerateGPUOptions } from '../hooks/use-form-initial-values';
|
||||
import AdvanceConfig from './advance-config';
|
||||
|
||||
interface DataFormProps {
|
||||
@@ -29,8 +25,7 @@ interface DataFormProps {
|
||||
sourceDisable?: boolean;
|
||||
backendOptions?: Global.BaseOption<string>[];
|
||||
sourceList?: Global.BaseOption<string>[];
|
||||
gpuOptions: any[];
|
||||
modelFileOptions?: any[];
|
||||
clusterList: Global.BaseOption<number>[];
|
||||
fields?: string[];
|
||||
onValuesChange?: (changedValues: any, allValues: any) => void;
|
||||
onSourceChange?: (value: string) => void;
|
||||
@@ -38,11 +33,6 @@ interface DataFormProps {
|
||||
onBackendChange?: (value: string) => void;
|
||||
}
|
||||
|
||||
const SEARCH_SOURCE = [
|
||||
modelSourceMap.huggingface_value,
|
||||
modelSourceMap.modelscope_value
|
||||
];
|
||||
|
||||
const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||
const {
|
||||
action,
|
||||
@@ -51,13 +41,13 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||
sourceDisable = true,
|
||||
backendOptions,
|
||||
sourceList,
|
||||
gpuOptions = [],
|
||||
modelFileOptions = [],
|
||||
clusterList = [],
|
||||
fields = ['source'],
|
||||
onSourceChange,
|
||||
onValuesChange,
|
||||
onOk
|
||||
} = props;
|
||||
const { getGPUOptionList, gpuOptions } = useGenerateGPUOptions();
|
||||
const { getRuleMessage } = useAppUtils();
|
||||
const [form] = Form.useForm();
|
||||
const intl = useIntl();
|
||||
@@ -143,6 +133,10 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||
onSourceChange?.(val);
|
||||
};
|
||||
|
||||
const handleClusterChange = (value: number) => {
|
||||
getGPUOptionList({ clusterId: value });
|
||||
};
|
||||
|
||||
const handleOnValuesChange = async (changedValues: any, allValues: any) => {
|
||||
const fieldName = Object.keys(changedValues)[0];
|
||||
|
||||
@@ -172,6 +166,9 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||
},
|
||||
getFieldsValue: () => {
|
||||
return form.getFieldsValue();
|
||||
},
|
||||
getGPUOptionList(params: { clusterId: number }) {
|
||||
getGPUOptionList(params);
|
||||
}
|
||||
};
|
||||
},
|
||||
@@ -179,78 +176,96 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||
);
|
||||
|
||||
return (
|
||||
<Form
|
||||
name="deployModel"
|
||||
form={form}
|
||||
onFinish={handleOk}
|
||||
preserve={false}
|
||||
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
|
||||
<FormInnerContext.Provider
|
||||
value={{
|
||||
onBackendChange: handleBackendChange,
|
||||
onValuesChange: onValuesChange,
|
||||
gpuOptions: gpuOptions
|
||||
}}
|
||||
>
|
||||
<Form.Item<FormData>
|
||||
name="name"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: getRuleMessage('input', 'common.table.name')
|
||||
}
|
||||
]}
|
||||
<Form
|
||||
name="deployModel"
|
||||
form={form}
|
||||
onFinish={handleOk}
|
||||
preserve={false}
|
||||
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>
|
||||
name="source"
|
||||
name="name"
|
||||
rules={[
|
||||
{
|
||||
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
|
||||
onChange={handleOnSourceChange}
|
||||
disabled={sourceDisable}
|
||||
label={intl.formatMessage({
|
||||
id: 'models.form.source'
|
||||
})}
|
||||
options={sourceList ?? sourceOptions}
|
||||
onChange={handleClusterChange}
|
||||
label="Cluster"
|
||||
options={clusterList}
|
||||
required
|
||||
></SealSelect>
|
||||
}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<FormInnerContext.Provider
|
||||
value={{
|
||||
onBackendChange: handleBackendChange,
|
||||
onValuesChange: onValuesChange,
|
||||
gpuOptions: gpuOptions
|
||||
}}
|
||||
>
|
||||
<HuggingFaceForm></HuggingFaceForm>
|
||||
<LocalPathForm></LocalPathForm>
|
||||
</FormInnerContext.Provider>
|
||||
{/* <Form.Item name="backend" rules={[{ required: true }]}>
|
||||
{/* <Form.Item name="backend" rules={[{ required: true }]}>
|
||||
<SealSelect
|
||||
required
|
||||
onChange={handleBackendChange}
|
||||
@@ -291,25 +306,26 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||
}
|
||||
></SealSelect>
|
||||
</Form.Item> */}
|
||||
<CatalogFrom></CatalogFrom>
|
||||
<Form.Item<FormData> name="description">
|
||||
<SealInput.TextArea
|
||||
scaleSize={true}
|
||||
label={intl.formatMessage({
|
||||
id: 'common.table.description'
|
||||
})}
|
||||
></SealInput.TextArea>
|
||||
</Form.Item>
|
||||
<AdvanceConfig
|
||||
form={form}
|
||||
gpuOptions={gpuOptions}
|
||||
isGGUF={isGGUF}
|
||||
action={action}
|
||||
source={props.source}
|
||||
backendOptions={backendOptions}
|
||||
handleBackendChange={handleBackendChange}
|
||||
></AdvanceConfig>
|
||||
</Form>
|
||||
<CatalogFrom></CatalogFrom>
|
||||
<Form.Item<FormData> name="description">
|
||||
<SealInput.TextArea
|
||||
scaleSize={true}
|
||||
label={intl.formatMessage({
|
||||
id: 'common.table.description'
|
||||
})}
|
||||
></SealInput.TextArea>
|
||||
</Form.Item>
|
||||
<AdvanceConfig
|
||||
form={form}
|
||||
gpuOptions={gpuOptions}
|
||||
isGGUF={isGGUF}
|
||||
action={action}
|
||||
source={props.source}
|
||||
backendOptions={backendOptions}
|
||||
handleBackendChange={handleBackendChange}
|
||||
></AdvanceConfig>
|
||||
</Form>
|
||||
</FormInnerContext.Provider>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ 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 { ProviderValueMap } from '@/pages/cluster-management/config';
|
||||
import { CloseOutlined } from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button } from 'antd';
|
||||
@@ -17,11 +18,8 @@ import {
|
||||
} from '../config';
|
||||
import { FormContext } from '../config/form-context';
|
||||
import { CatalogSpec, FormData, ListItem, SourceType } from '../config/types';
|
||||
import {
|
||||
checkOnlyAscendNPU,
|
||||
useCheckCompatibility,
|
||||
useGenerateFormEditInitialValues
|
||||
} from '../hooks';
|
||||
import { useCheckCompatibility } from '../hooks';
|
||||
import useFormInitialValues from '../hooks/use-form-initial-values';
|
||||
import ColumnWrapper from './column-wrapper';
|
||||
import CompatibilityAlert from './compatible-alert';
|
||||
import DataForm from './data-form';
|
||||
@@ -104,10 +102,9 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
handleOnValuesChange,
|
||||
warningStatus
|
||||
} = useCheckCompatibility();
|
||||
const { getClusterList, clusterList } = useFormInitialValues();
|
||||
const intl = useIntl();
|
||||
const { getGPUList } = useGenerateFormEditInitialValues();
|
||||
const form = useRef<any>({});
|
||||
const [gpuOptions, setGpuOptions] = useState<any[]>([]);
|
||||
const [isGGUF, setIsGGUF] = useState<boolean>(false);
|
||||
const [sourceList, setSourceList] = useState<any[]>([]);
|
||||
const [backendList, setBackendList] = useState<any[]>([]);
|
||||
@@ -161,15 +158,6 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
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));
|
||||
};
|
||||
|
||||
@@ -360,6 +348,15 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
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 () => {
|
||||
try {
|
||||
axiosToken.current?.cancel?.();
|
||||
@@ -407,7 +404,10 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
size: defaultSpec.size,
|
||||
backend: defaultSpec.backend
|
||||
});
|
||||
initFormDataBySource(defaultSpec);
|
||||
initFormDataBySource({
|
||||
...defaultSpec,
|
||||
cluster_id: initClusterId()
|
||||
});
|
||||
|
||||
const name = _.toLower(current.name).replace(/\s/g, '-') || '';
|
||||
form.current.setFieldValue('name', name);
|
||||
@@ -420,6 +420,7 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
const allValues = generateSubmitData({
|
||||
...defaultSpec,
|
||||
categories: _.get(current, 'categories.0', null),
|
||||
cluster_id: initClusterId(),
|
||||
name
|
||||
});
|
||||
handleCheckCompatibility(allValues);
|
||||
@@ -484,7 +485,9 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
fetchSpecData();
|
||||
setTimeout(() => {
|
||||
fetchSpecData();
|
||||
}, 100);
|
||||
}
|
||||
return () => {
|
||||
axiosToken.current?.cancel?.();
|
||||
@@ -498,9 +501,7 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
}, [open, current]);
|
||||
|
||||
useEffect(() => {
|
||||
getGPUList().then((data) => {
|
||||
setGpuOptions(data);
|
||||
});
|
||||
getClusterList();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -608,7 +609,7 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
sourceDisable={false}
|
||||
backendOptions={backendList}
|
||||
sourceList={sourceList}
|
||||
gpuOptions={gpuOptions}
|
||||
clusterList={clusterList}
|
||||
onBackendChange={handleBackendChange}
|
||||
onSourceChange={handleSourceChange}
|
||||
onValuesChange={onValuesChange}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
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 { ProviderValueMap } from '@/pages/cluster-management/config';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button } from 'antd';
|
||||
import _ from 'lodash';
|
||||
@@ -68,10 +68,12 @@ type AddModalProps = {
|
||||
source: SourceType;
|
||||
isGGUF?: boolean;
|
||||
width?: string | number;
|
||||
gpuOptions: any[];
|
||||
modelFileOptions: any[];
|
||||
initialValues?: any;
|
||||
deploymentType?: 'modelList' | 'modelFiles';
|
||||
clusterList: Global.BaseOption<
|
||||
number,
|
||||
{ provider: string; state: string | number }
|
||||
>[];
|
||||
onOk: (values: FormData) => void;
|
||||
onCancel: () => void;
|
||||
};
|
||||
@@ -95,7 +97,8 @@ const AddModal: FC<AddModalProps> = (props) => {
|
||||
action,
|
||||
width = 600,
|
||||
deploymentType = 'modelList',
|
||||
initialValues
|
||||
initialValues,
|
||||
clusterList
|
||||
} = props || {};
|
||||
const SEARCH_SOURCE = [
|
||||
modelSourceMap.huggingface_value,
|
||||
@@ -113,7 +116,7 @@ const AddModal: FC<AddModalProps> = (props) => {
|
||||
warningStatus,
|
||||
submitAnyway
|
||||
} = useCheckCompatibility();
|
||||
const { onSelectModel } = useSelectModel({ gpuOptions: props.gpuOptions });
|
||||
const { onSelectModel } = useSelectModel({ gpuOptions: [] });
|
||||
const form = useRef<any>({});
|
||||
const intl = useIntl();
|
||||
const [selectedModel, setSelectedModel] = useState<any>({});
|
||||
@@ -161,15 +164,6 @@ const AddModal: FC<AddModalProps> = (props) => {
|
||||
evaluateStateRef.current = state;
|
||||
};
|
||||
|
||||
const updateEvaluateState = (state: EvaluateProccessType) => {
|
||||
const currentRequestModelId = evaluateStateRef.current.requestModelId;
|
||||
setEvaluteState({
|
||||
...evaluateStateRef.current,
|
||||
state
|
||||
});
|
||||
return currentRequestModelId;
|
||||
};
|
||||
|
||||
const handleOnValuesChange = (data: {
|
||||
changedValues: any;
|
||||
allValues: any;
|
||||
@@ -199,71 +193,6 @@ const AddModal: FC<AddModalProps> = (props) => {
|
||||
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 = () => {
|
||||
cancelEvaluate();
|
||||
modelFileRef.current?.cancelRequest();
|
||||
@@ -441,22 +370,37 @@ const AddModal: FC<AddModalProps> = (props) => {
|
||||
onCancel?.();
|
||||
}, [onCancel]);
|
||||
|
||||
const initClusterId = () => {
|
||||
const cluster_id =
|
||||
clusterList?.find((item) => item.provider === ProviderValueMap.Custom)
|
||||
?.value || clusterList?.[0]?.value;
|
||||
|
||||
return cluster_id;
|
||||
};
|
||||
|
||||
const handleOnOpen = () => {
|
||||
if (props.deploymentType === 'modelFiles') {
|
||||
form.current?.form?.setFieldsValue({
|
||||
...props.initialValues
|
||||
...props.initialValues,
|
||||
cluster_id: initClusterId()
|
||||
});
|
||||
handleOnValuesChange?.({
|
||||
changedValues: {},
|
||||
allValues: props.initialValues,
|
||||
allValues: {
|
||||
...props.initialValues,
|
||||
cluster_id: initClusterId()
|
||||
},
|
||||
source: source
|
||||
});
|
||||
} else {
|
||||
let backend = checkOnlyAscendNPU(props.gpuOptions)
|
||||
let backend = checkOnlyAscendNPU([])
|
||||
? backendOptionsMap.ascendMindie
|
||||
: backendOptionsMap.vllm;
|
||||
|
||||
form.current?.setFieldValue?.('backend', backend);
|
||||
form.current?.setFieldsValue?.({
|
||||
backend,
|
||||
cluster_id: initClusterId()
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -483,6 +427,9 @@ const AddModal: FC<AddModalProps> = (props) => {
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
handleOnOpen();
|
||||
form.current?.getGPUOptionList?.({
|
||||
clusterId: initClusterId()
|
||||
});
|
||||
} else {
|
||||
cancelEvaluate();
|
||||
clearCahceFormValues();
|
||||
@@ -495,7 +442,7 @@ const AddModal: FC<AddModalProps> = (props) => {
|
||||
message: []
|
||||
});
|
||||
};
|
||||
}, [open, props.gpuOptions.length]);
|
||||
}, [open, clusterList]);
|
||||
|
||||
return (
|
||||
<GSDrawer
|
||||
@@ -534,7 +481,7 @@ const AddModal: FC<AddModalProps> = (props) => {
|
||||
handleOnSelectModelAfterEvaluate
|
||||
}
|
||||
displayEvaluateStatus={displayEvaluateStatus}
|
||||
gpuOptions={props.gpuOptions}
|
||||
gpuOptions={[]}
|
||||
></SearchModel>
|
||||
</ColumnWrapper>
|
||||
<Separator></Separator>
|
||||
@@ -559,8 +506,6 @@ const AddModal: FC<AddModalProps> = (props) => {
|
||||
value={{
|
||||
isGGUF: isGGUF,
|
||||
pageAction: action,
|
||||
modelFileOptions: props.modelFileOptions,
|
||||
gpuOptions: props.gpuOptions,
|
||||
onValuesChange: onValuesChange
|
||||
}}
|
||||
>
|
||||
@@ -609,12 +554,11 @@ const AddModal: FC<AddModalProps> = (props) => {
|
||||
initialValues={initialValues}
|
||||
source={source}
|
||||
action={action}
|
||||
clusterList={clusterList}
|
||||
selectedModel={selectedModel}
|
||||
onOk={handleOnOk}
|
||||
ref={form}
|
||||
isGGUF={isGGUF}
|
||||
gpuOptions={props.gpuOptions}
|
||||
modelFileOptions={props.modelFileOptions}
|
||||
onBackendChange={handleBackendChange}
|
||||
onValuesChange={onValuesChange}
|
||||
></DataForm>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import CheckboxField from '@/components/seal-form/checkbox-field';
|
||||
import SealCascader from '@/components/seal-form/seal-cascader';
|
||||
import SealSelect from '@/components/seal-form/seal-select';
|
||||
import TooltipList from '@/components/tooltip-list';
|
||||
@@ -7,8 +6,7 @@ import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import React from 'react';
|
||||
import { backendOptionsMap } from '../config';
|
||||
import { useFormContext } from '../config/form-context';
|
||||
import { FormData } from '../config/types';
|
||||
import { useFormContext, useFormInnerContext } from '../config/form-context';
|
||||
import GPUCard from './gpu-card';
|
||||
|
||||
const scheduleTypeTips = [
|
||||
@@ -30,13 +28,8 @@ const scheduleTypeTips = [
|
||||
|
||||
const Performance: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const {
|
||||
onValuesChange,
|
||||
onQuantizationChange,
|
||||
gpuOptions,
|
||||
source,
|
||||
quantizationOptions
|
||||
} = useFormContext();
|
||||
const { gpuOptions } = useFormInnerContext();
|
||||
const { onValuesChange, onQuantizationChange } = useFormContext();
|
||||
const { getRuleMessage } = useAppUtils();
|
||||
const form = Form.useFormInstance();
|
||||
|
||||
@@ -167,7 +160,7 @@ const Performance: React.FC = () => {
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
<div style={{ paddingBottom: 22, paddingLeft: 10 }}>
|
||||
{/* <div style={{ paddingBottom: 22, paddingLeft: 10 }}>
|
||||
<Form.Item<FormData>
|
||||
name="optimize_long_prompt"
|
||||
valuePropName="checked"
|
||||
@@ -192,7 +185,7 @@ const Performance: React.FC = () => {
|
||||
})}
|
||||
></CheckboxField>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div> */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -45,8 +45,7 @@ const CheckboxField: React.FC<{
|
||||
|
||||
const Scaling: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const { onValuesChange, onQuantizationChange, source, quantizationOptions } =
|
||||
useFormContext();
|
||||
const { onValuesChange, onQuantizationChange } = useFormContext();
|
||||
const { getRuleMessage } = useAppUtils();
|
||||
const form = Form.useFormInstance();
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
|
||||
try {
|
||||
const params = {
|
||||
Name: `${searchInputRef.current}`,
|
||||
tags: [],
|
||||
tags: ['gptq'],
|
||||
tasks: filterTaskRef.current
|
||||
? ([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 { ListItem as WorkerListItem } from '@/pages/resources/config/types';
|
||||
import { handleBatchRequest } from '@/utils';
|
||||
import {
|
||||
IS_FIRST_LOGIN,
|
||||
readState,
|
||||
writeState
|
||||
} from '@/utils/localstore/index';
|
||||
import {
|
||||
DownOutlined,
|
||||
QuestionCircleOutlined,
|
||||
@@ -26,16 +21,7 @@ import {
|
||||
} from '@ant-design/icons';
|
||||
import { PageContainer } from '@ant-design/pro-components';
|
||||
import { useIntl, useNavigate } from '@umijs/max';
|
||||
import {
|
||||
Button,
|
||||
Empty,
|
||||
Input,
|
||||
Select,
|
||||
Space,
|
||||
Tooltip,
|
||||
Typography,
|
||||
message
|
||||
} from 'antd';
|
||||
import { Button, Input, Select, Space, Tooltip, message } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import { useAtom } from 'jotai';
|
||||
import _ from 'lodash';
|
||||
@@ -76,7 +62,7 @@ import {
|
||||
ModelInstanceListItem,
|
||||
SourceType
|
||||
} from '../config/types';
|
||||
import { useGenerateFormEditInitialValues } from '../hooks';
|
||||
import useFormInitialValues from '../hooks/use-form-initial-values';
|
||||
import APIAccessInfoModal from './api-access-info';
|
||||
import DeployModal from './deploy-modal';
|
||||
import Instances from './instances';
|
||||
@@ -88,6 +74,7 @@ interface ModelsProps {
|
||||
handleNameChange: (e: any) => void;
|
||||
handleShowSizeChange?: (page: number, size: number) => void;
|
||||
handlePageChange: (page: number, pageSize: number | undefined) => void;
|
||||
handleClusterChange: (value: number) => void;
|
||||
handleDeleteSuccess: () => void;
|
||||
handleCategoryChange: (val: any) => void;
|
||||
onViewLogs: () => void;
|
||||
@@ -103,29 +90,12 @@ interface ModelsProps {
|
||||
};
|
||||
deleteIds?: number[];
|
||||
workerList: WorkerListItem[];
|
||||
modelFileOptions: any[];
|
||||
catalogList?: any[];
|
||||
dataSource: ListItem[];
|
||||
loading: boolean;
|
||||
loadend: boolean;
|
||||
total: number;
|
||||
}
|
||||
|
||||
const clusterList = [
|
||||
{
|
||||
label: 'Custom',
|
||||
value: 'custom'
|
||||
},
|
||||
{
|
||||
label: 'Kubernetes',
|
||||
value: 'kubernetes'
|
||||
},
|
||||
{
|
||||
label: 'Digital Ocean',
|
||||
value: 'digital_ocean'
|
||||
}
|
||||
];
|
||||
|
||||
const statusList = [
|
||||
{
|
||||
label: 'Running',
|
||||
@@ -164,34 +134,27 @@ const Models: React.FC<ModelsProps> = ({
|
||||
onCancelViewLogs,
|
||||
handleCategoryChange,
|
||||
handleOnToggleExpandAll,
|
||||
handleClusterChange,
|
||||
onStop,
|
||||
onStart,
|
||||
modelFileOptions,
|
||||
deleteIds,
|
||||
dataSource,
|
||||
workerList,
|
||||
catalogList,
|
||||
queryParams,
|
||||
loading,
|
||||
loadend,
|
||||
total
|
||||
}) => {
|
||||
const { getGPUList, generateFormValues, gpuDeviceList } =
|
||||
useGenerateFormEditInitialValues();
|
||||
const { getGPUOptionList, generateFormValues, clusterList, getClusterList } =
|
||||
useFormInitialValues();
|
||||
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
|
||||
const [updateFormInitials, setUpdateFormInitials] = useState<{
|
||||
gpuOptions: any[];
|
||||
modelFileOptions?: any[];
|
||||
data: any;
|
||||
isGGUF: boolean;
|
||||
}>({
|
||||
gpuOptions: [],
|
||||
modelFileOptions: [],
|
||||
data: {},
|
||||
isGGUF: false
|
||||
});
|
||||
const [isFirstLogin, setIsFirstLogin] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [expandAtom, setExpandAtom] = useAtom(modelsExpandKeysAtom);
|
||||
const intl = useIntl();
|
||||
const navigate = useNavigate();
|
||||
@@ -218,17 +181,13 @@ const Models: React.FC<ModelsProps> = ({
|
||||
width: number | string;
|
||||
hasLinuxWorker?: boolean;
|
||||
source: SourceType;
|
||||
gpuOptions: any[];
|
||||
isGGUF?: boolean;
|
||||
modelFileOptions?: any[];
|
||||
}>({
|
||||
show: false,
|
||||
hasLinuxWorker: false,
|
||||
width: 600,
|
||||
isGGUF: false,
|
||||
source: modelSourceMap.huggingface_value as SourceType,
|
||||
gpuOptions: [],
|
||||
modelFileOptions: []
|
||||
source: modelSourceMap.huggingface_value as SourceType
|
||||
});
|
||||
const currentData = useRef<ListItem>({} as ListItem);
|
||||
const [currentInstance, setCurrentInstance] = useState<{
|
||||
@@ -243,17 +202,6 @@ const Models: React.FC<ModelsProps> = ({
|
||||
});
|
||||
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(() => {
|
||||
if (deleteIds?.length) {
|
||||
rowSelection.removeSelectedKey(deleteIds);
|
||||
@@ -262,7 +210,9 @@ const Models: React.FC<ModelsProps> = ({
|
||||
|
||||
useEffect(() => {
|
||||
const getData = async () => {
|
||||
await getGPUList();
|
||||
const res = await getClusterList();
|
||||
const clusterId = res[0]?.value;
|
||||
await getGPUOptionList({ clusterId });
|
||||
};
|
||||
getData();
|
||||
return () => {
|
||||
@@ -470,10 +420,8 @@ const Models: React.FC<ModelsProps> = ({
|
||||
}, []);
|
||||
|
||||
const handleEdit = async (row: ListItem) => {
|
||||
const initialValues = generateFormValues(row, gpuDeviceList.current);
|
||||
const initialValues = generateFormValues(row, []);
|
||||
setUpdateFormInitials({
|
||||
gpuOptions: gpuDeviceList.current,
|
||||
modelFileOptions: modelFileOptions,
|
||||
data: initialValues,
|
||||
isGGUF: row.backend === backendOptionsMap.llamaBox
|
||||
});
|
||||
@@ -584,9 +532,7 @@ const Models: React.FC<ModelsProps> = ({
|
||||
if (config) {
|
||||
setOpenDeployModal({
|
||||
...config,
|
||||
hasLinuxWorker: hasLinuxWorker,
|
||||
gpuOptions: gpuDeviceList.current,
|
||||
modelFileOptions: modelFileOptions
|
||||
hasLinuxWorker: hasLinuxWorker
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -652,7 +598,10 @@ const Models: React.FC<ModelsProps> = ({
|
||||
span: 3,
|
||||
render: (text: string, record: ListItem) => (
|
||||
<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>
|
||||
)
|
||||
},
|
||||
@@ -724,31 +673,6 @@ const Models: React.FC<ModelsProps> = ({
|
||||
];
|
||||
}, [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(
|
||||
(expanded: boolean) => {
|
||||
const keys = dataSource.map((item) => item.id);
|
||||
@@ -760,33 +684,6 @@ const Models: React.FC<ModelsProps> = ({
|
||||
[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 (
|
||||
<>
|
||||
<PageContainer
|
||||
@@ -831,17 +728,9 @@ const Models: React.FC<ModelsProps> = ({
|
||||
style={{ width: 160 }}
|
||||
size="large"
|
||||
maxTagCount={1}
|
||||
onChange={handleClusterChange}
|
||||
options={clusterList}
|
||||
></Select>
|
||||
<Select
|
||||
allowClear
|
||||
showSearch={false}
|
||||
placeholder="Running Replicas"
|
||||
style={{ width: 140 }}
|
||||
size="large"
|
||||
maxTagCount={1}
|
||||
options={statusList}
|
||||
></Select>
|
||||
<Button
|
||||
type="text"
|
||||
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
||||
@@ -918,6 +807,7 @@ const Models: React.FC<ModelsProps> = ({
|
||||
action={PageAction.EDIT}
|
||||
title={intl.formatMessage({ id: 'models.title.edit' })}
|
||||
updateFormInitials={updateFormInitials}
|
||||
clusterList={clusterList}
|
||||
onCancel={handleModalCancel}
|
||||
onOk={handleModalOk}
|
||||
></UpdateModel>
|
||||
@@ -929,8 +819,7 @@ const Models: React.FC<ModelsProps> = ({
|
||||
width={openDeployModal.width}
|
||||
isGGUF={openDeployModal.isGGUF}
|
||||
hasLinuxWorker={openDeployModal.hasLinuxWorker}
|
||||
gpuOptions={openDeployModal.gpuOptions}
|
||||
modelFileOptions={openDeployModal.modelFileOptions || []}
|
||||
clusterList={clusterList}
|
||||
onCancel={handleDeployModalCancel}
|
||||
onOk={handleCreateModel}
|
||||
></DeployModal>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import ModalFooter from '@/components/modal-footer';
|
||||
import SealInput from '@/components/seal-form/seal-input';
|
||||
import SealSelect from '@/components/seal-form/seal-select';
|
||||
import TooltipList from '@/components/tooltip-list';
|
||||
import { PageAction } from '@/config';
|
||||
import { PageActionType } from '@/config/types';
|
||||
import useAppUtils from '@/hooks/use-app-utils';
|
||||
@@ -10,9 +9,7 @@ import { Button, Form, Modal } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import React, { useEffect, useMemo, useRef } from 'react';
|
||||
import {
|
||||
backendLabelMap,
|
||||
backendOptionsMap,
|
||||
backendTipsList,
|
||||
updateExcludeFields as excludeFields,
|
||||
getSourceRepoConfigValue,
|
||||
modelSourceMap,
|
||||
@@ -24,6 +21,7 @@ import { FormData, ListItem } from '../config/types';
|
||||
import HuggingFaceForm from '../forms/hugging-face';
|
||||
import LocalPathForm from '../forms/local-path';
|
||||
import { useCheckCompatibility } from '../hooks';
|
||||
import { useGenerateGPUOptions } from '../hooks/use-form-initial-values';
|
||||
import AdvanceConfig from './advance-config';
|
||||
import ColumnWrapper from './column-wrapper';
|
||||
import CompatibilityAlert from './compatible-alert';
|
||||
@@ -34,9 +32,12 @@ type AddModalProps = {
|
||||
open: boolean;
|
||||
updateFormInitials: {
|
||||
data?: ListItem;
|
||||
gpuOptions: any[];
|
||||
isGGUF: boolean;
|
||||
};
|
||||
clusterList: Global.BaseOption<
|
||||
number,
|
||||
{ provider: string; state: string | number }
|
||||
>[];
|
||||
onOk: (values: FormData) => void;
|
||||
onCancel: () => void;
|
||||
};
|
||||
@@ -48,7 +49,8 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
|
||||
open,
|
||||
onOk,
|
||||
onCancel,
|
||||
updateFormInitials: { gpuOptions, isGGUF, data: formData }
|
||||
clusterList,
|
||||
updateFormInitials: { isGGUF, data: formData }
|
||||
} = props || {};
|
||||
const intl = useIntl();
|
||||
const {
|
||||
@@ -58,12 +60,17 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
|
||||
checkTokenRef,
|
||||
warningStatus
|
||||
} = useCheckCompatibility();
|
||||
const { getGPUOptionList, gpuOptions } = useGenerateGPUOptions();
|
||||
|
||||
const { getRuleMessage } = useAppUtils();
|
||||
const [form] = Form.useForm();
|
||||
const submitAnyway = useRef<boolean>(false);
|
||||
const originFormData = useRef<any>(null);
|
||||
|
||||
const handleClusterChange = (value: number) => {
|
||||
getGPUOptionList({ clusterId: value });
|
||||
};
|
||||
|
||||
const setOriginalFormData = () => {
|
||||
if (!originFormData.current) {
|
||||
originFormData.current = _.cloneDeep(formData);
|
||||
@@ -336,48 +343,53 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
|
||||
onValuesChange: handleManulOnValuesChange
|
||||
}}
|
||||
>
|
||||
<Form
|
||||
name="updateModalForm"
|
||||
form={form}
|
||||
onFinish={handleOk}
|
||||
onValuesChange={onValuesChange}
|
||||
scrollToFirstError={true}
|
||||
preserve={false}
|
||||
clearOnDestroy={true}
|
||||
initialValues={{
|
||||
...formData
|
||||
}}
|
||||
style={{
|
||||
padding: 'var(--ant-modal-content-padding)',
|
||||
paddingBlock: 0
|
||||
<FormInnerContext.Provider
|
||||
value={{
|
||||
onBackendChange: handleBackendChange,
|
||||
gpuOptions: gpuOptions
|
||||
}}
|
||||
>
|
||||
<Form.Item<FormData>
|
||||
name="name"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: getRuleMessage('input', 'common.table.name')
|
||||
}
|
||||
]}
|
||||
<Form
|
||||
name="updateModalForm"
|
||||
form={form}
|
||||
onFinish={handleOk}
|
||||
onValuesChange={onValuesChange}
|
||||
scrollToFirstError={true}
|
||||
preserve={false}
|
||||
clearOnDestroy={true}
|
||||
initialValues={{
|
||||
...formData
|
||||
}}
|
||||
style={{
|
||||
padding: 'var(--ant-modal-content-padding)',
|
||||
paddingBlock: 0
|
||||
}}
|
||||
>
|
||||
<SealInput.Input
|
||||
label={intl.formatMessage({
|
||||
id: 'common.table.name'
|
||||
})}
|
||||
required
|
||||
></SealInput.Input>
|
||||
</Form.Item>
|
||||
<Form.Item<FormData>
|
||||
name="source"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: getRuleMessage('select', 'models.form.source')
|
||||
}
|
||||
]}
|
||||
>
|
||||
{action === PageAction.EDIT && (
|
||||
<Form.Item<FormData>
|
||||
name="name"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: getRuleMessage('input', 'common.table.name')
|
||||
}
|
||||
]}
|
||||
>
|
||||
<SealInput.Input
|
||||
label={intl.formatMessage({
|
||||
id: 'common.table.name'
|
||||
})}
|
||||
required
|
||||
></SealInput.Input>
|
||||
</Form.Item>
|
||||
<Form.Item<FormData>
|
||||
name="source"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: getRuleMessage('select', 'models.form.source')
|
||||
}
|
||||
]}
|
||||
>
|
||||
<SealSelect
|
||||
disabled={true}
|
||||
label={intl.formatMessage({
|
||||
@@ -386,82 +398,46 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
|
||||
options={sourceOptions}
|
||||
required
|
||||
></SealSelect>
|
||||
)}
|
||||
</Form.Item>
|
||||
<FormInnerContext.Provider
|
||||
value={{
|
||||
onBackendChange: handleBackendChange,
|
||||
gpuOptions: gpuOptions
|
||||
}}
|
||||
>
|
||||
</Form.Item>
|
||||
|
||||
<HuggingFaceForm></HuggingFaceForm>
|
||||
<LocalPathForm></LocalPathForm>
|
||||
</FormInnerContext.Provider>
|
||||
<Form.Item name="backend" rules={[{ required: true }]}>
|
||||
<SealSelect
|
||||
required
|
||||
onChange={handleAsyncBackendChange}
|
||||
label={intl.formatMessage({ id: 'models.form.backend' })}
|
||||
description={<TooltipList list={backendTipsList}></TooltipList>}
|
||||
options={[
|
||||
<Form.Item<FormData>
|
||||
name="cluster_id"
|
||||
rules={[
|
||||
{
|
||||
label: backendLabelMap[backendOptionsMap.llamaBox],
|
||||
value: backendOptionsMap.llamaBox,
|
||||
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
|
||||
required: true,
|
||||
message: getRuleMessage('select', 'Cluster', false)
|
||||
}
|
||||
]}
|
||||
disabled={
|
||||
action === PageAction.EDIT &&
|
||||
formData?.source !== modelSourceMap.local_path_value &&
|
||||
!isVllmOrAscend
|
||||
>
|
||||
{
|
||||
<SealSelect
|
||||
onChange={handleClusterChange}
|
||||
label="Cluster"
|
||||
options={clusterList}
|
||||
required
|
||||
></SealSelect>
|
||||
}
|
||||
></SealSelect>
|
||||
</Form.Item>
|
||||
<Form.Item<FormData> name="description">
|
||||
<SealInput.TextArea
|
||||
scaleSize={true}
|
||||
label={intl.formatMessage({
|
||||
id: 'common.table.description'
|
||||
})}
|
||||
></SealInput.TextArea>
|
||||
</Form.Item>
|
||||
</Form.Item>
|
||||
<Form.Item<FormData> name="description">
|
||||
<SealInput.TextArea
|
||||
scaleSize={true}
|
||||
label={intl.formatMessage({
|
||||
id: 'common.table.description'
|
||||
})}
|
||||
></SealInput.TextArea>
|
||||
</Form.Item>
|
||||
|
||||
<AdvanceConfig
|
||||
form={form}
|
||||
gpuOptions={gpuOptions}
|
||||
action={PageAction.EDIT}
|
||||
source={formData?.source || ''}
|
||||
isGGUF={formData?.backend === backendOptionsMap.llamaBox}
|
||||
></AdvanceConfig>
|
||||
</Form>
|
||||
<AdvanceConfig
|
||||
form={form}
|
||||
gpuOptions={gpuOptions}
|
||||
action={PageAction.EDIT}
|
||||
source={formData?.source || ''}
|
||||
isGGUF={formData?.backend === backendOptionsMap.llamaBox}
|
||||
></AdvanceConfig>
|
||||
</Form>
|
||||
</FormInnerContext.Provider>
|
||||
</FormContext.Provider>
|
||||
</ColumnWrapper>
|
||||
</Modal>
|
||||
|
||||
@@ -8,7 +8,6 @@ interface FormContextProps {
|
||||
pageAction: PageActionType;
|
||||
sizeOptions?: Global.BaseOption<number>[];
|
||||
quantizationOptions?: Global.BaseOption<string>[];
|
||||
modelFileOptions?: any[];
|
||||
gpuOptions?: any[];
|
||||
onSizeChange?: (val: number) => void;
|
||||
onQuantizationChange?: (val: string) => void;
|
||||
|
||||
@@ -20,6 +20,7 @@ export interface ListItem {
|
||||
name: string;
|
||||
description: string;
|
||||
id: number;
|
||||
cluster_id: number;
|
||||
local_path?: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
@@ -69,6 +70,7 @@ export interface FormData {
|
||||
description: string;
|
||||
optimize_long_prompt: boolean;
|
||||
enable_speculative_decoding: boolean;
|
||||
cluster_id: number;
|
||||
}
|
||||
|
||||
interface ComputedResourceClaim {
|
||||
|
||||
@@ -20,6 +20,7 @@ type AddModalProps = {
|
||||
width?: string | number;
|
||||
hasLinuxWorker?: boolean;
|
||||
workersList: Global.BaseOption<number>[];
|
||||
workerOptions: any[];
|
||||
onOk: (values: FormData) => void;
|
||||
onCancel: () => void;
|
||||
};
|
||||
@@ -33,7 +34,8 @@ const DownloadModel: React.FC<AddModalProps> = (props) => {
|
||||
onCancel,
|
||||
hasLinuxWorker,
|
||||
source,
|
||||
width = 600
|
||||
width = 600,
|
||||
workerOptions
|
||||
} = props || {};
|
||||
const SEARCH_SOURCE = [
|
||||
modelSourceMap.huggingface_value,
|
||||
@@ -227,6 +229,7 @@ const DownloadModel: React.FC<AddModalProps> = (props) => {
|
||||
onOk={handleOk}
|
||||
source={source}
|
||||
workersList={workersList}
|
||||
workerOptions={workerOptions}
|
||||
></TargetForm>
|
||||
</>
|
||||
</ColumnWrapper>
|
||||
|
||||
@@ -13,11 +13,12 @@ interface TargetFormProps {
|
||||
ref?: any;
|
||||
workersList: Global.BaseOption<number>[];
|
||||
source: string;
|
||||
workerOptions: any[];
|
||||
onOk: (values: any) => void;
|
||||
}
|
||||
|
||||
const TargetForm: React.FC<TargetFormProps> = forwardRef((props, ref) => {
|
||||
const { onOk, source, workersList } = props;
|
||||
const { onOk, source, workersList, workerOptions } = props;
|
||||
const { getRuleMessage } = useAppUtils();
|
||||
const intl = useIntl();
|
||||
const [form] = Form.useForm();
|
||||
@@ -115,13 +116,24 @@ const TargetForm: React.FC<TargetFormProps> = forwardRef((props, ref) => {
|
||||
}
|
||||
]}
|
||||
>
|
||||
{
|
||||
<SealSelect
|
||||
label="Worker"
|
||||
options={workersList}
|
||||
required
|
||||
></SealSelect>
|
||||
}
|
||||
<SealSelect
|
||||
label="Worker"
|
||||
options={workersList}
|
||||
required
|
||||
></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>
|
||||
{source !== modelSourceMap.local_path_value && (
|
||||
<Form.Item<FormData>
|
||||
|
||||
@@ -1,30 +1,19 @@
|
||||
import { createAxiosToken } from '@/hooks/use-chunk-request';
|
||||
import { queryModelFilesList, queryWorkersList } from '@/pages/resources/apis';
|
||||
import {
|
||||
WorkerStatusMap,
|
||||
WorkerStatusMapValue
|
||||
} from '@/pages/resources/config';
|
||||
import { queryModelFilesList } from '@/pages/resources/apis';
|
||||
import { ListItem as WorkerListItem } from '@/pages/resources/config/types';
|
||||
import { convertFileSize } from '@/utils';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { useDebounceFn } from 'ahooks';
|
||||
import _ from 'lodash';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { evaluationsModelSpec, queryGPUList } from '../apis';
|
||||
import { evaluationsModelSpec } from '../apis';
|
||||
import {
|
||||
backendOptionsMap,
|
||||
getSourceRepoConfigValue,
|
||||
modelSourceMap,
|
||||
modelTaskMap,
|
||||
setSourceRepoConfigValue
|
||||
modelTaskMap
|
||||
} from '../config';
|
||||
import { handleRecognizeAudioModel } from '../config/audio-catalog';
|
||||
import {
|
||||
EvaluateResult,
|
||||
FormData,
|
||||
GPUListItem,
|
||||
ListItem
|
||||
} from '../config/types';
|
||||
import { EvaluateResult, FormData } from '../config/types';
|
||||
|
||||
export type MessageStatus = {
|
||||
show: boolean;
|
||||
@@ -41,123 +30,6 @@ export type WarningStausOptions = {
|
||||
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 = () => {
|
||||
const getModelFileList = async () => {
|
||||
try {
|
||||
@@ -514,25 +386,6 @@ export const useCheckCompatibility = () => {
|
||||
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 = () => {
|
||||
cacheFormValuesRef.current = {};
|
||||
};
|
||||
@@ -585,11 +438,6 @@ export const useCheckCompatibility = () => {
|
||||
return res;
|
||||
};
|
||||
|
||||
const { run: debounceHandleValuesChange } = useDebounceFn(
|
||||
handleOnValuesChange,
|
||||
{ wait: 500 }
|
||||
);
|
||||
|
||||
const cancelEvaluate = () => {
|
||||
// update the requestId to cancel the current evaluation
|
||||
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 { queryWorkersList } from '@/pages/resources/apis';
|
||||
import { ListItem as WokerListItem } from '@/pages/resources/config/types';
|
||||
import { IS_FIRST_LOGIN, readState } from '@/utils/localstore';
|
||||
import _ from 'lodash';
|
||||
import qs from 'query-string';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
MODELS_API,
|
||||
MODEL_INSTANCE_API,
|
||||
queryCatalogItemSpec,
|
||||
queryCatalogList,
|
||||
queryModelsInstances,
|
||||
queryModelsList
|
||||
} from './apis';
|
||||
import TableList from './components/table-list';
|
||||
import { backendOptionsMap } from './config';
|
||||
import { ListItem } from './config/types';
|
||||
import { useGenerateModelFileOptions } from './hooks';
|
||||
|
||||
const Models: React.FC = () => {
|
||||
const { getModelFileList, generateModelFileOptions } =
|
||||
useGenerateModelFileOptions();
|
||||
const { setChunkRequest, createAxiosToken } = useSetChunkRequest();
|
||||
const { setChunkRequest: setModelInstanceChunkRequest } =
|
||||
useSetChunkRequest();
|
||||
@@ -41,9 +34,7 @@ const Models: React.FC = () => {
|
||||
total: 0
|
||||
});
|
||||
|
||||
const [catalogList, setCatalogList] = useState<any[]>([]);
|
||||
const [workerList, setWorkerList] = useState<WokerListItem[]>([]);
|
||||
const [modelFileOptions, setModelFileOptions] = useState<any[]>([]);
|
||||
const chunkRequedtRef = useRef<any>();
|
||||
const chunkInstanceRequedtRef = useRef<any>();
|
||||
const isPageHidden = useRef(false);
|
||||
@@ -168,20 +159,20 @@ const Models: React.FC = () => {
|
||||
[queryParams]
|
||||
);
|
||||
|
||||
const handleQueryChange = (params: any) => {
|
||||
setQueryParams({
|
||||
...queryParams,
|
||||
...params
|
||||
});
|
||||
fetchData({ query: { ...queryParams, ...params } });
|
||||
};
|
||||
|
||||
const handlePageChange = useCallback(
|
||||
(page: number, pageSize: number | undefined) => {
|
||||
setQueryParams({
|
||||
...queryParams,
|
||||
handleQueryChange({
|
||||
page: page,
|
||||
perPage: pageSize || 10
|
||||
});
|
||||
fetchData({
|
||||
query: {
|
||||
...queryParams,
|
||||
page: page,
|
||||
perPage: pageSize || 10
|
||||
}
|
||||
});
|
||||
},
|
||||
[queryParams]
|
||||
);
|
||||
@@ -269,18 +260,10 @@ const Models: React.FC = () => {
|
||||
);
|
||||
|
||||
const debounceUpdateFilter = _.debounce((e: any) => {
|
||||
setQueryParams({
|
||||
...queryParams,
|
||||
handleQueryChange({
|
||||
page: 1,
|
||||
search: e.target.value
|
||||
});
|
||||
fetchData({
|
||||
query: {
|
||||
...queryParams,
|
||||
page: 1,
|
||||
search: e.target.value
|
||||
}
|
||||
});
|
||||
createModelsChunkRequest({
|
||||
search: e.target.value,
|
||||
categories: queryParams.categories
|
||||
@@ -289,27 +272,27 @@ const Models: React.FC = () => {
|
||||
|
||||
const handleNameChange = useCallback(debounceUpdateFilter, [queryParams]);
|
||||
|
||||
const handleCategoryChange = useCallback(
|
||||
async (value: any) => {
|
||||
setQueryParams({
|
||||
...queryParams,
|
||||
page: 1,
|
||||
categories: value
|
||||
});
|
||||
fetchData({
|
||||
query: {
|
||||
...queryParams,
|
||||
page: 1,
|
||||
categories: value
|
||||
}
|
||||
});
|
||||
createModelsChunkRequest({
|
||||
search: queryParams.search,
|
||||
categories: value
|
||||
});
|
||||
},
|
||||
[queryParams]
|
||||
);
|
||||
const handleCategoryChange = async (value: any) => {
|
||||
handleQueryChange({
|
||||
page: 1,
|
||||
categories: value
|
||||
});
|
||||
createModelsChunkRequest({
|
||||
search: queryParams.search,
|
||||
categories: value
|
||||
});
|
||||
};
|
||||
|
||||
const handleClusterChange = async (value: any) => {
|
||||
handleQueryChange({
|
||||
page: 1,
|
||||
cluster_id: value
|
||||
});
|
||||
createModelsChunkRequest({
|
||||
search: queryParams.search,
|
||||
cluster_id: value
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
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 [modelRes, workerRes, modelFileList] = await Promise.all([
|
||||
const [modelRes, workerRes] = await Promise.all([
|
||||
getTableData(),
|
||||
getWorkerList(),
|
||||
getModelFileList()
|
||||
getWorkerList()
|
||||
]);
|
||||
const dataList = generateModelFileOptions(
|
||||
modelFileList,
|
||||
workerRes.items || []
|
||||
);
|
||||
|
||||
setDataSource({
|
||||
dataList: modelRes.items || [],
|
||||
loading: false,
|
||||
@@ -402,7 +342,6 @@ const Models: React.FC = () => {
|
||||
deletedIds: []
|
||||
});
|
||||
setWorkerList(workerRes.items || []);
|
||||
setModelFileOptions(dataList);
|
||||
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
@@ -464,6 +403,7 @@ const Models: React.FC = () => {
|
||||
dataSource={dataSource.dataList}
|
||||
handleNameChange={handleNameChange}
|
||||
handleCategoryChange={handleCategoryChange}
|
||||
handleClusterChange={handleClusterChange}
|
||||
handleSearch={handleSearch}
|
||||
handlePageChange={handlePageChange}
|
||||
handleDeleteSuccess={fetchData}
|
||||
@@ -478,8 +418,6 @@ const Models: React.FC = () => {
|
||||
total={dataSource.total}
|
||||
deleteIds={dataSource.deletedIds}
|
||||
workerList={workerList}
|
||||
modelFileOptions={modelFileOptions}
|
||||
catalogList={catalogList}
|
||||
></TableList>
|
||||
</TableContext.Provider>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user