chore: catalog card style

This commit is contained in:
jialin
2025-01-03 20:51:22 +08:00
parent d9319f5734
commit 1a792254ca
16 changed files with 696 additions and 299 deletions
@@ -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 [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 handleSetIsGGUF = (flag: boolean) => {
setIsGGUF(flag);
if (flag) {
debounceFetchModelFiles();
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
};
}
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>