|
|
|
|
@ -1,12 +1,14 @@
|
|
|
|
|
import ModalFooter from '@/components/modal-footer';
|
|
|
|
|
import { PageActionType } from '@/config/types';
|
|
|
|
|
import { createAxiosToken } from '@/hooks/use-chunk-request';
|
|
|
|
|
import { CloseOutlined } from '@ant-design/icons';
|
|
|
|
|
import { useIntl } from '@umijs/max';
|
|
|
|
|
import { Button, Drawer } from 'antd';
|
|
|
|
|
import { debounce } from 'lodash';
|
|
|
|
|
import _ from 'lodash';
|
|
|
|
|
import { memo, useCallback, useEffect, useRef, useState } from 'react';
|
|
|
|
|
import { backendOptionsMap, modelSourceMap } from '../config';
|
|
|
|
|
import { FormData, ListItem } from '../config/types';
|
|
|
|
|
import { queryCatalogItemSpec } from '../apis';
|
|
|
|
|
import { backendOptionsMap, modelSourceMap, sourceOptions } from '../config';
|
|
|
|
|
import { CatalogSpec, FormData, ListItem } from '../config/types';
|
|
|
|
|
import ColumnWrapper from './column-wrapper';
|
|
|
|
|
import DataForm from './data-form';
|
|
|
|
|
|
|
|
|
|
@ -17,27 +19,27 @@ type AddModalProps = {
|
|
|
|
|
data?: ListItem;
|
|
|
|
|
source: string;
|
|
|
|
|
width?: string | number;
|
|
|
|
|
current?: any;
|
|
|
|
|
onOk: (values: FormData) => void;
|
|
|
|
|
onCancel: () => void;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const steps = [
|
|
|
|
|
const backendOptions = [
|
|
|
|
|
{
|
|
|
|
|
element: '#filterGGUF',
|
|
|
|
|
popover: {
|
|
|
|
|
title: '筛选模型',
|
|
|
|
|
description: 'Select a model from the list'
|
|
|
|
|
}
|
|
|
|
|
label: `llama-box`,
|
|
|
|
|
value: backendOptionsMap.llamaBox
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
element: '#backend-field',
|
|
|
|
|
popover: {
|
|
|
|
|
title: '选择推理后端',
|
|
|
|
|
description: 'Select a model from the list'
|
|
|
|
|
}
|
|
|
|
|
label: 'vLLM',
|
|
|
|
|
value: backendOptionsMap.vllm
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: 'vox-box',
|
|
|
|
|
value: backendOptionsMap.voxBox
|
|
|
|
|
}
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const defaultQuant = ['Q4_K_M'];
|
|
|
|
|
const AddModal: React.FC<AddModalProps> = (props) => {
|
|
|
|
|
const {
|
|
|
|
|
title,
|
|
|
|
|
@ -46,41 +48,166 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
|
|
|
|
onCancel,
|
|
|
|
|
source,
|
|
|
|
|
action,
|
|
|
|
|
current,
|
|
|
|
|
width = 600
|
|
|
|
|
} = props || {};
|
|
|
|
|
const SEARCH_SOURCE = [];
|
|
|
|
|
|
|
|
|
|
const form = useRef<any>({});
|
|
|
|
|
const intl = useIntl();
|
|
|
|
|
const [selectedModel, setSelectedModel] = useState<any>({});
|
|
|
|
|
const [collapsed, setCollapsed] = useState<boolean>(false);
|
|
|
|
|
const [loadingModel, setLoadingModel] = useState<boolean>(false);
|
|
|
|
|
const [isGGUF, setIsGGUF] = useState<boolean>(false);
|
|
|
|
|
const modelFileRef = useRef<any>(null);
|
|
|
|
|
const [loadfinish, setLoadfinish] = useState<boolean>(false);
|
|
|
|
|
|
|
|
|
|
const handleSelectModelFile = useCallback((item: any) => {
|
|
|
|
|
form.current?.setFieldValue?.('file_name', item.fakeName);
|
|
|
|
|
setLoadfinish(true);
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
const handleOnSelectModel = (item: any) => {
|
|
|
|
|
setSelectedModel(item);
|
|
|
|
|
};
|
|
|
|
|
const [isGGUF, setIsGGUF] = useState<boolean>(false);
|
|
|
|
|
const [sourceList, setSourceList] = useState<any[]>([]);
|
|
|
|
|
const [backendList, setBackendList] = useState<any[]>([]);
|
|
|
|
|
const [sizeOptions, setSizeOptions] = useState<any[]>([]);
|
|
|
|
|
const [quantizationOptions, setQuantizationOptions] = useState<any[]>([]);
|
|
|
|
|
const sourceGroupMap = useRef<any>({});
|
|
|
|
|
const axiosToken = useRef<any>(null);
|
|
|
|
|
const selectSpecRef = useRef<CatalogSpec>({} as CatalogSpec);
|
|
|
|
|
|
|
|
|
|
const handleSumit = () => {
|
|
|
|
|
form.current?.submit?.();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const debounceFetchModelFiles = debounce(() => {
|
|
|
|
|
modelFileRef.current?.fetchModelFiles?.();
|
|
|
|
|
}, 300);
|
|
|
|
|
const getModelFile = (spec: CatalogSpec) => {
|
|
|
|
|
let modelInfo = {};
|
|
|
|
|
if (spec.source === modelSourceMap.huggingface_value) {
|
|
|
|
|
modelInfo = {
|
|
|
|
|
huggingface_repo_id: spec?.huggingface_repo_id,
|
|
|
|
|
huggingface_filename: spec?.huggingface_filename
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (spec.source === modelSourceMap.modelscope_value) {
|
|
|
|
|
modelInfo = {
|
|
|
|
|
model_scope_model_id: spec?.model_scope_model_id,
|
|
|
|
|
model_scope_file_path: spec?.model_scope_file_path
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleSetIsGGUF = (flag: boolean) => {
|
|
|
|
|
setIsGGUF(flag);
|
|
|
|
|
if (flag) {
|
|
|
|
|
debounceFetchModelFiles();
|
|
|
|
|
if (spec.source === modelSourceMap.ollama_library_value) {
|
|
|
|
|
modelInfo = {
|
|
|
|
|
ollama_library_model_name: spec?.ollama_library_model_name
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (spec.source === modelSourceMap.local_path_value) {
|
|
|
|
|
modelInfo = {
|
|
|
|
|
local_path: spec?.local_path
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return modelInfo;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const getModelSpec = (data: {
|
|
|
|
|
source: string;
|
|
|
|
|
backend: string;
|
|
|
|
|
size: number;
|
|
|
|
|
quantization: string;
|
|
|
|
|
}) => {
|
|
|
|
|
const groupList = sourceGroupMap.current[data.source];
|
|
|
|
|
const spec = _.find(groupList, (item: CatalogSpec) => {
|
|
|
|
|
if (data.size && data.quantization) {
|
|
|
|
|
return (
|
|
|
|
|
item.size === data.size &&
|
|
|
|
|
item.backend === data.backend &&
|
|
|
|
|
item.quantization === data.quantization
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (data.size) {
|
|
|
|
|
return item.size === data.size && item.backend === data.backend;
|
|
|
|
|
}
|
|
|
|
|
if (data.quantization) {
|
|
|
|
|
return (
|
|
|
|
|
item.quantization === data.quantization &&
|
|
|
|
|
item.backend === data.backend
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return item.backend === data.backend;
|
|
|
|
|
});
|
|
|
|
|
selectSpecRef.current = spec;
|
|
|
|
|
return {
|
|
|
|
|
..._.omit(spec, ['name']),
|
|
|
|
|
categories: _.get(spec, 'categories.0', null)
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const initFormDataBySource = (data: CatalogSpec) => {
|
|
|
|
|
selectSpecRef.current = data;
|
|
|
|
|
form.current?.setFieldsValue({
|
|
|
|
|
..._.omit(data, ['name']),
|
|
|
|
|
categories: _.get(data, 'categories.0', null)
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleSetSizeOptions = (data: { source: string; backend: string }) => {
|
|
|
|
|
const groupList = sourceGroupMap.current[source];
|
|
|
|
|
const list = _.filter(groupList, (item: CatalogSpec) => item.size);
|
|
|
|
|
const sizeGroup = _.groupBy(
|
|
|
|
|
_.filter(list, (item: CatalogSpec) => {
|
|
|
|
|
return item.backend === data.backend;
|
|
|
|
|
}),
|
|
|
|
|
'size'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const sizeList = _.keys(sizeGroup).map((size: string) => {
|
|
|
|
|
return {
|
|
|
|
|
label: `${size}B`,
|
|
|
|
|
value: _.toNumber(size)
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
setSizeOptions(sizeList);
|
|
|
|
|
return sizeList;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleSetQuantizationOptions = (data: {
|
|
|
|
|
source: string;
|
|
|
|
|
size: number;
|
|
|
|
|
backend: string;
|
|
|
|
|
}) => {
|
|
|
|
|
const groupList = sourceGroupMap.current[data.source];
|
|
|
|
|
console.log('groupList====', data, groupList);
|
|
|
|
|
const sizeGroup = _.filter(groupList, (item: CatalogSpec) => {
|
|
|
|
|
return item.size === data.size && item.backend === data.backend;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const quantizationList = _.map(sizeGroup, (item: CatalogSpec) => {
|
|
|
|
|
return {
|
|
|
|
|
label: item.quantization,
|
|
|
|
|
value: item.quantization
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
setQuantizationOptions(quantizationList);
|
|
|
|
|
return quantizationList;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleSetBackendOptions = (source: string) => {
|
|
|
|
|
const groupList = sourceGroupMap.current[source];
|
|
|
|
|
const backendGroup = _.groupBy(groupList, 'backend');
|
|
|
|
|
console.log('backendGroup====', backendGroup, source);
|
|
|
|
|
|
|
|
|
|
const backendList = _.filter(backendOptions, (item: any) => {
|
|
|
|
|
return backendGroup[item.value];
|
|
|
|
|
});
|
|
|
|
|
setBackendList(backendList);
|
|
|
|
|
return backendList;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleSourceChange = (source: string) => {
|
|
|
|
|
const defaultSpec = _.get(sourceGroupMap.current, `${source}.0`, {});
|
|
|
|
|
console.log('source====', source, defaultSpec);
|
|
|
|
|
initFormDataBySource(defaultSpec);
|
|
|
|
|
handleSetSizeOptions({
|
|
|
|
|
source: source,
|
|
|
|
|
backend: defaultSpec.backend
|
|
|
|
|
});
|
|
|
|
|
handleSetQuantizationOptions({
|
|
|
|
|
source: source,
|
|
|
|
|
size: defaultSpec.size,
|
|
|
|
|
backend: defaultSpec.backend
|
|
|
|
|
});
|
|
|
|
|
// set form value
|
|
|
|
|
initFormDataBySource(defaultSpec);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleBackendChange = (backend: string) => {
|
|
|
|
|
@ -91,29 +218,138 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
|
|
|
|
if (backend === backendOptionsMap.llamaBox) {
|
|
|
|
|
setIsGGUF(true);
|
|
|
|
|
}
|
|
|
|
|
const sizeList = handleSetSizeOptions({
|
|
|
|
|
source: form.current.getFieldValue('source'),
|
|
|
|
|
backend: backend
|
|
|
|
|
});
|
|
|
|
|
const quantizaList = handleSetQuantizationOptions({
|
|
|
|
|
source: form.current.getFieldValue('source'),
|
|
|
|
|
size: _.get(sizeList, '0.value', 0),
|
|
|
|
|
backend: backend
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const data = getModelSpec({
|
|
|
|
|
source: form.current.getFieldValue('source'),
|
|
|
|
|
backend: backend,
|
|
|
|
|
size: _.get(sizeList, '0.value', 0),
|
|
|
|
|
quantization:
|
|
|
|
|
_.find(quantizaList, (item: { label: string; value: string }) =>
|
|
|
|
|
defaultQuant.includes(item.value)
|
|
|
|
|
)?.value || _.get(quantizaList, '0.value', '')
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
form.current.setFieldsValue({
|
|
|
|
|
...data
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const fetchSpecData = async () => {
|
|
|
|
|
try {
|
|
|
|
|
axiosToken.current?.cancel?.();
|
|
|
|
|
axiosToken.current = createAxiosToken();
|
|
|
|
|
const res: any = await queryCatalogItemSpec(
|
|
|
|
|
{
|
|
|
|
|
id: current.id
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
token: axiosToken.current.token
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
const groupList = _.groupBy(res.items, 'source');
|
|
|
|
|
sourceGroupMap.current = groupList;
|
|
|
|
|
|
|
|
|
|
const sources = _.filter(sourceOptions, (item: any) => {
|
|
|
|
|
return groupList[item.value];
|
|
|
|
|
});
|
|
|
|
|
const source = _.get(sources, '0.value', '');
|
|
|
|
|
const defaultSpec =
|
|
|
|
|
_.find(groupList[source], (item: CatalogSpec) => {
|
|
|
|
|
return defaultQuant.includes(item.quantization);
|
|
|
|
|
}) || _.get(groupList, `${source}.0`, {});
|
|
|
|
|
|
|
|
|
|
setSourceList(sources);
|
|
|
|
|
handleSetBackendOptions(source);
|
|
|
|
|
handleSetSizeOptions({
|
|
|
|
|
source: source,
|
|
|
|
|
backend: defaultSpec.backend
|
|
|
|
|
});
|
|
|
|
|
handleSetQuantizationOptions({
|
|
|
|
|
source: source,
|
|
|
|
|
size: defaultSpec.size,
|
|
|
|
|
backend: defaultSpec.backend
|
|
|
|
|
});
|
|
|
|
|
initFormDataBySource(defaultSpec);
|
|
|
|
|
form.current.setFieldValue(
|
|
|
|
|
'name',
|
|
|
|
|
_.toLower(current.name).replace(/\s/g, '-') || ''
|
|
|
|
|
);
|
|
|
|
|
if (defaultSpec.backend === backendOptionsMap.vllm) {
|
|
|
|
|
setIsGGUF(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (defaultSpec.backend === backendOptionsMap.llamaBox) {
|
|
|
|
|
setIsGGUF(true);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
// ignore
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleOnQuantizationChange = (val: string) => {
|
|
|
|
|
const data = getModelSpec({
|
|
|
|
|
source: form.current.getFieldValue('source'),
|
|
|
|
|
backend: form.current.getFieldValue('backend'),
|
|
|
|
|
size: form.current.getFieldValue('size'),
|
|
|
|
|
quantization: val
|
|
|
|
|
});
|
|
|
|
|
form.current.setFieldsValue({
|
|
|
|
|
...data
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleOnSizeChange = (val: number) => {
|
|
|
|
|
const list = handleSetQuantizationOptions({
|
|
|
|
|
source: form.current.getFieldValue('source'),
|
|
|
|
|
backend: form.current.getFieldValue('backend'),
|
|
|
|
|
size: val
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const data = getModelSpec({
|
|
|
|
|
source: form.current.getFieldValue('source'),
|
|
|
|
|
backend: form.current.getFieldValue('backend'),
|
|
|
|
|
size: val,
|
|
|
|
|
quantization:
|
|
|
|
|
_.find(list, (item: { label: string; value: string }) =>
|
|
|
|
|
defaultQuant.includes(item.value)
|
|
|
|
|
)?.value || _.get(list, '0.value', '')
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// set form data
|
|
|
|
|
form.current.setFieldsValue({
|
|
|
|
|
...data
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleOk = (values: FormData) => {
|
|
|
|
|
onOk({
|
|
|
|
|
...values,
|
|
|
|
|
...getModelFile(selectSpecRef.current)
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleCancel = useCallback(() => {
|
|
|
|
|
onCancel?.();
|
|
|
|
|
axiosToken.current?.cancel?.();
|
|
|
|
|
}, [onCancel]);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
handleSelectModelFile({ fakeName: '' });
|
|
|
|
|
}, [selectedModel]);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!open) {
|
|
|
|
|
setIsGGUF(false);
|
|
|
|
|
form.current?.setFieldValue?.('backend', backendOptionsMap.vllm);
|
|
|
|
|
} else if (source === modelSourceMap.ollama_library_value) {
|
|
|
|
|
form.current?.setFieldValue?.('backend', backendOptionsMap.llamaBox);
|
|
|
|
|
setIsGGUF(true);
|
|
|
|
|
if (open) {
|
|
|
|
|
fetchSpecData();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
setSelectedModel({});
|
|
|
|
|
axiosToken.current?.cancel?.();
|
|
|
|
|
};
|
|
|
|
|
}, [open, source]);
|
|
|
|
|
}, [open, current]);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Drawer
|
|
|
|
|
@ -170,11 +406,20 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
|
|
|
|
<DataForm
|
|
|
|
|
source={source}
|
|
|
|
|
action={action}
|
|
|
|
|
selectedModel={selectedModel}
|
|
|
|
|
onOk={onOk}
|
|
|
|
|
selectedModel={{}}
|
|
|
|
|
onOk={handleOk}
|
|
|
|
|
ref={form}
|
|
|
|
|
isGGUF={isGGUF}
|
|
|
|
|
byBuiltIn={true}
|
|
|
|
|
sourceDisable={false}
|
|
|
|
|
backendOptions={backendList}
|
|
|
|
|
sourceList={sourceList}
|
|
|
|
|
quantizationOptions={quantizationOptions}
|
|
|
|
|
sizeOptions={sizeOptions}
|
|
|
|
|
onBackendChange={handleBackendChange}
|
|
|
|
|
onSourceChange={handleSourceChange}
|
|
|
|
|
onQuantizationChange={handleOnQuantizationChange}
|
|
|
|
|
onSizeChange={handleOnSizeChange}
|
|
|
|
|
></DataForm>
|
|
|
|
|
</>
|
|
|
|
|
</ColumnWrapper>
|
|
|
|
|
|