chore: check compatibility ux

This commit is contained in:
jialin
2025-03-31 16:21:40 +08:00
parent bd6c689d91
commit f83e49bd3d
30 changed files with 1192 additions and 583 deletions
+26 -317
View File
@@ -1,65 +1,45 @@
import IconFont from '@/components/icon-font';
import SealAutoComplete from '@/components/seal-form/auto-complete';
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';
import { createAxiosToken } from '@/hooks/use-chunk-request';
import { useIntl } from '@umijs/max';
import { Form, Typography } from 'antd';
import { Form } from 'antd';
import _ from 'lodash';
import React, {
forwardRef,
useImperativeHandle,
useMemo,
useRef,
useState
} from 'react';
import { evaluationsModelSpec } from '../apis';
import React, { forwardRef, useImperativeHandle, useState } from 'react';
import {
HuggingFaceTaskMap,
ModelscopeTaskMap,
backendOptionsMap,
backendTipsList,
excludeFields,
getSourceRepoConfigValue,
localPathTipsList,
modelSourceMap,
modelTaskMap,
ollamaModelOptions,
sourceOptions
} from '../config';
import { identifyModelTask } from '../config/audio-catalog';
import { EvaluateResult, FormData } from '../config/types';
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 OllamaForm from '../forms/ollama-library';
import AdvanceConfig from './advance-config';
interface DataFormProps {
initialValues?: any;
ref?: any;
source: string;
source: SourceType;
action: PageActionType;
selectedModel: any;
isGGUF: boolean;
sizeOptions?: Global.BaseOption<number>[];
quantizationOptions?: Global.BaseOption<string>[];
sourceDisable?: boolean;
byBuiltIn?: boolean;
backendOptions?: Global.BaseOption<string>[];
sourceList?: Global.BaseOption<string>[];
gpuOptions: any[];
modelFileOptions?: any[];
fields?: string[];
handleUpdateWarning?: (params: {
backend: string;
localPath: string;
source: string;
}) => any;
handleShowCompatibleAlert?: (data: EvaluateResult | null) => void;
onValuesChange?: (changedValues: any, allValues: any) => void;
onSizeChange?: (val: number) => void;
onQuantizationChange?: (val: string) => void;
onSourceChange?: (value: string) => void;
onOk: (values: FormData) => void;
onBackendChange?: (value: string) => void;
@@ -78,15 +58,11 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
sourceDisable = true,
backendOptions,
sourceList,
byBuiltIn,
gpuOptions = [],
modelFileOptions = [],
sizeOptions = [],
quantizationOptions = [],
fields = ['source'],
handleUpdateWarning,
handleShowCompatibleAlert,
onSourceChange,
onValuesChange,
onOk
} = props;
console.log('modelFileOptions--------', modelFileOptions);
@@ -99,8 +75,6 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
text2speech: false,
speech2text: false
});
const checkTokenRef = useRef<any>(null);
const localPathCache = useRef<string>('');
const handleSumit = () => {
form.submit();
@@ -153,268 +127,6 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
}
};
const handleOnFocus = () => {
localPathCache.current = form.getFieldValue('local_path');
};
const handleEvaluate = async (data: any) => {
try {
checkTokenRef.current?.cancel();
checkTokenRef.current = createAxiosToken();
const evalution = await evaluationsModelSpec(
{
model_specs: [
{
..._.omit(data, ['scheduleType']),
categories: data.categories ? [data.categories] : []
}
]
},
{
token: checkTokenRef.current.token
}
);
return evalution.results?.[0];
} catch (error) {
console.log('error=====', error);
return null;
}
};
// trigger from local_path change or backend change
const handleBackendChangeHook = async () => {
const localPath = form.getFieldValue?.('local_path');
const backend = form.getFieldValue?.('backend');
const res = handleUpdateWarning?.({
backend,
localPath: localPath,
source: props.source
});
if (!res.show) {
const values = form.getFieldsValue?.();
const data = getSourceRepoConfigValue(props.source, values);
const evalutionData = await handleEvaluate(data.values);
handleShowCompatibleAlert?.(evalutionData);
}
};
const handleLocalPathBlur = (e: any) => {
const value = e.target.value;
console.log('handleLocalPathBlur:', e, localPathCache.current);
if (value === localPathCache.current && value) {
return;
}
const isEndwithGGUF = _.endsWith(value, '.gguf');
const isBlobFile = value.split('/').pop().includes('sha256');
let backend = backendOptionsMap.llamaBox;
if (!isEndwithGGUF && !isBlobFile) {
backend = backendOptionsMap.vllm;
}
form.setFieldValue('backend', backend);
handleBackendChangeHook();
props.onBackendChange?.(backend);
};
const renderHuggingfaceFields = () => {
return (
<>
<Form.Item<FormData>
name="repo_id"
key="repo_id"
rules={[
{
required: true,
message: getRuleMessage('input', 'models.form.repoid')
}
]}
>
<SealInput.Input
label={intl.formatMessage({ id: 'models.form.repoid' })}
required
disabled={true}
></SealInput.Input>
</Form.Item>
{isGGUF && (
<Form.Item<FormData>
name="file_name"
key="file_name"
rules={[
{
required: true,
message: getRuleMessage('input', 'models.form.filename')
}
]}
>
<SealInput.Input
label={intl.formatMessage({ id: 'models.form.filename' })}
required
disabled={true}
></SealInput.Input>
</Form.Item>
)}
</>
);
};
const renderOllamaModelFields = () => {
return (
<>
<Form.Item<FormData>
name="ollama_library_model_name"
key="ollama_library_model_name"
rules={[
{
required: true,
message: getRuleMessage('input', 'models.table.name')
}
]}
>
<SealAutoComplete
allowClear
filterOption
defaultActiveFirstOption
disabled={false}
options={ollamaModelOptions}
description={
<span>
<span>
{intl.formatMessage({ id: 'models.form.ollamalink' })}
</span>
<Typography.Link
className="flex-center"
href="https://www.ollama.com/library"
target="_blank"
>
<IconFont
type="icon-external-link"
className="font-size-14"
></IconFont>
</Typography.Link>
</span>
}
label={intl.formatMessage({ id: 'model.form.ollama.model' })}
placeholder={intl.formatMessage({ id: 'model.form.ollamaholder' })}
required
></SealAutoComplete>
</Form.Item>
</>
);
};
const renderLocalPathFields = () => {
return (
<>
<Form.Item<FormData>
name="local_path"
key="local_path"
rules={[
{
required: true,
message: getRuleMessage('input', 'models.form.filePath')
}
]}
>
<SealAutoComplete
allowClear
required
filterOption
defaultActiveFirstOption
options={modelFileOptions}
onBlur={handleLocalPathBlur}
onFocus={handleOnFocus}
label={intl.formatMessage({ id: 'models.form.filePath' })}
description={<TooltipList list={localPathTipsList}></TooltipList>}
></SealAutoComplete>
</Form.Item>
</>
);
};
const handleSizeChange = (val: any) => {
props.onSizeChange?.(val);
};
const handleOnQuantizationChange = (val: any) => {
props.onQuantizationChange?.(val);
};
// from catalog
const renderFieldsFromCatalog = useMemo(() => {
if (!byBuiltIn && !sizeOptions?.length && !quantizationOptions?.length) {
return null;
}
return (
<>
{sizeOptions?.length > 0 && (
<Form.Item<FormData>
name="size"
key="size"
rules={[
{
required: true,
message: getRuleMessage('input', 'size', false)
}
]}
>
<SealSelect
filterOption
onChange={handleSizeChange}
defaultActiveFirstOption
disabled={false}
options={sizeOptions}
label="Size"
required
></SealSelect>
</Form.Item>
)}
{quantizationOptions?.length > 0 && (
<Form.Item<FormData>
name="quantization"
key="quantization"
rules={[
{
required: true,
message: getRuleMessage('select', 'quantization', false)
}
]}
>
<SealSelect
filterOption
defaultActiveFirstOption
disabled={false}
options={quantizationOptions}
onChange={handleOnQuantizationChange}
label="Quantization"
required
></SealSelect>
</Form.Item>
)}
</>
);
}, [sizeOptions, quantizationOptions, byBuiltIn]);
const renderFieldsBySource = useMemo(() => {
// from catalog
if (byBuiltIn) {
return null;
}
if (SEARCH_SOURCE.includes(props.source)) {
return renderHuggingfaceFields();
}
if (props.source === modelSourceMap.ollama_library_value) {
return renderOllamaModelFields();
}
if (props.source === modelSourceMap.local_path_value) {
return renderLocalPathFields();
}
return null;
}, [props.source, isGGUF, byBuiltIn, intl]);
const handleSetGPUIds = (backend: string) => {
if (backend === backendOptionsMap.llamaBox) {
return;
@@ -441,7 +153,6 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
}
form.setFieldsValue(updates);
handleSetGPUIds(val);
handleBackendChangeHook();
props.onBackendChange?.(val);
};
@@ -474,7 +185,7 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
return {};
};
const handleOk = (formdata: FormData) => {
const handleOk = async (formdata: FormData) => {
let data = _.cloneDeep(formdata);
if (data.categories) {
data.categories = [data.categories];
@@ -482,10 +193,12 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
data.categories = [];
}
const gpuSelector = generateGPUIds(data);
onOk({
const allValues = {
..._.omit(data, ['scheduleType']),
...gpuSelector
});
};
onOk(allValues);
};
const handleOnSourceChange = (val: string) => {
@@ -493,19 +206,7 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
};
const handleOnValuesChange = async (changedValues: any, allValues: any) => {
const keys = Object.keys(changedValues);
const isExcludeField = keys.some((key) => excludeFields.includes(key));
if (
!isExcludeField &&
!_.has(changedValues, 'backend') &&
!_.has(changedValues, 'local_path')
) {
const values = form.getFieldsValue?.();
const data = getSourceRepoConfigValue(props.source, values);
const evalutionData = await handleEvaluate(data.values);
handleShowCompatibleAlert?.(evalutionData);
}
onValuesChange?.(changedValues, allValues);
};
useImperativeHandle(
@@ -595,7 +296,15 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
</Form.Item>
)}
{renderFieldsBySource}
<FormInnerContext.Provider
value={{
onBackendChange: handleBackendChange
}}
>
<HuggingFaceForm></HuggingFaceForm>
<OllamaForm></OllamaForm>
<LocalPathForm></LocalPathForm>
</FormInnerContext.Provider>
<Form.Item name="backend" rules={[{ required: true }]}>
<SealSelect
required
@@ -637,7 +346,7 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
}
></SealSelect>
</Form.Item>
{renderFieldsFromCatalog}
<CatalogFrom></CatalogFrom>
<Form.Item<FormData>
name="replicas"
rules={[