style: gguf model ux

This commit is contained in:
jialin
2025-10-17 10:15:55 +08:00
parent 37a3aa75cc
commit 005295ac97
19 changed files with 203 additions and 89 deletions
+112 -25
View File
@@ -1,11 +1,14 @@
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 { useMemoizedFn } from 'ahooks';
import { Button } from 'antd';
import _ from 'lodash';
import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { FC, useEffect, useMemo, useRef, useState } from 'react';
import styled from 'styled-components';
import ColumnWrapper from '../../_components/column-wrapper';
import { defaultFormValues, deployFormKeyMap, modelSourceMap } from '../config';
@@ -20,14 +23,21 @@ import {
} from '../hooks';
import useCheckBackend from '../hooks/use-check-backend';
import CompatibilityAlert from './compatible-alert';
import GGUFResult from './gguf-result';
import HFModelFile from './hf-model-file';
import ModelCard from './model-card';
import SearchModel from './search-model';
import Separator from './separator';
import TitleWrapper from './title-wrapper';
const pickFieldsFromSpec = ['backend_version', 'backend_parameters', 'env'];
const dropFieldsFromForm = ['name', 'file_name', 'repo_id', 'backend'];
const dropFieldsFromForm = [
'name',
'huggingface_filename',
'model_scope_file_path',
'model_scope_model_id',
'huggingface_repo_id',
'backend'
];
const resetFields = ['worker_selector', 'env'];
const ModalFooterStyle = {
@@ -127,6 +137,11 @@ const AddModal: FC<AddModalProps> = (props) => {
const requestModelIdRef = useRef<number>(0);
const currentSelectedModel = useRef<any>({});
const { run: fetchModelFiles } = useDeferredRequest(
() => modelFileRef.current?.fetchModelFiles?.(),
100
);
const updateSelectedModel = (model: any) => {
currentSelectedModel.current = model;
setSelectedModel(model);
@@ -182,6 +197,78 @@ 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']),
huggingface_filename: item.fakeName,
model_scope_file_path: item.fakeName,
backend_parameters:
formValues.backend_parameters?.length > 0
? formValues.backend_parameters
: defaultSpec.backend_parameters || [],
backend_version:
formValues.backend_version || defaultSpec.backend_version,
env: formValues.env || defaultSpec.env,
categories: getCategory(item)
});
},
100
);
const handleSelectModelFile = async (
item: any,
options: { requestModelId: number; manual?: boolean }
) => {
const { requestModelId, manual } = options || {};
if (requestModelId !== getRequestId()) {
return;
}
console.log('handleSelectModelFile:', item, selectedModel);
const modelInfo = onSelectModel(selectedModel, props.source);
form.current?.setFieldsValue?.({
..._.omit(modelInfo, ['name']),
huggingface_filename: item.fakeName,
model_scope_file_path: item.fakeName,
backend_parameters: [],
backend_version: '',
backend: '',
env: {},
categories: getCategory(item)
});
// evaluate the form data when select a model file
// TODO: reset backend related fields when select a GGUF file
if (item.fakeName) {
// onSelectFile(item, modelInfo, manual);
}
};
const handleCancelFiles = () => {
cancelEvaluate();
modelFileRef.current?.cancelRequest();
@@ -242,8 +329,7 @@ const AddModal: FC<AddModalProps> = (props) => {
};
if (item.isGGUF) {
warningStatus.type = 'danger';
warningStatus.message = 'GGUF model is not supported.';
fetchModelFiles();
}
setWarningStatus(warningStatus, { override: true });
};
@@ -317,25 +403,21 @@ const AddModal: FC<AddModalProps> = (props) => {
};
const handleBackendChange = async (backend: string) => {
setIsGGUF(false);
const data = form.current.form.getFieldsValue?.();
const res = handleBackendChangeBefore(data);
if (res.show) {
return;
}
if (data.local_path || props.source !== modelSourceMap.local_path_value) {
// TODO confirm wheather it is gguf by model file not by backend
// TODO confirm whether it is gguf by model file not by backend
handleOnValuesChange?.({
changedValues: {},
// allValues:
// backend === backendOptionsMap.llamaBox
// ? data
// : _.omit(data, [
// 'cpu_offloading',
// 'distributed_inference_across_workers'
// ]),
allValues: data,
allValues: isGGUF
? data
: _.omit(data, [
'cpu_offloading',
'distributed_inference_across_workers'
]),
source: props.source
});
}
@@ -349,9 +431,9 @@ const AddModal: FC<AddModalProps> = (props) => {
});
};
const handleCancel = useCallback(() => {
const handleCancel = useMemoizedFn(() => {
onCancel?.();
}, [onCancel]);
});
const initClusterId = () => {
if (initialValues?.cluster_id) {
@@ -492,7 +574,7 @@ const AddModal: FC<AddModalProps> = (props) => {
<Separator></Separator>
</ColWrapper>
<ColWrapper>
<ColumnWrapper styles={{ container: { paddingTop: 0 } }}>
<ColumnWrapper styles={{ container: { padding: 0 } }}>
<ModelCard
selectedModel={selectedModel}
onCollapse={setCollapsed}
@@ -500,7 +582,16 @@ const AddModal: FC<AddModalProps> = (props) => {
modelSource={props.source}
setIsGGUF={handleSetIsGGUF}
></ModelCard>
{isGGUF && <GGUFResult></GGUFResult>}
{isGGUF && (
<HFModelFile
ref={modelFileRef}
selectedModel={selectedModel}
modelSource={props.source}
onSelectFile={handleSelectModelFile}
collapsed={collapsed}
></HFModelFile>
)}
</ColumnWrapper>
<Separator></Separator>
</ColWrapper>
@@ -532,11 +623,7 @@ const AddModal: FC<AddModalProps> = (props) => {
showOkBtn={!showExtraButton}
extra={
showExtraButton && (
<Button
type="primary"
onClick={handleSubmitAnyway}
disabled={isGGUF}
>
<Button type="primary" onClick={handleSubmitAnyway}>
{intl.formatMessage({
id: 'models.form.submit.anyway'
})}
@@ -19,7 +19,8 @@ import {
queryHuggingfaceModelFiles,
queryModelScopeModelFiles
} from '../apis';
import { backendOptionsMap, modelSourceMap } from '../config';
import { modelSourceMap } from '../config';
import { backendOptionsMap } from '../config/backend-parameters';
import '../style/hf-model-file.less';
import FileSkeleton from './file-skeleton';
import ModelFileItem from './model-file-item';
@@ -40,7 +41,6 @@ interface HFModelFileProps {
loadingModel?: boolean;
modelSource: string;
ref: any;
gpuOptions?: any[];
onSelectFile?: (
file: any,
options: { requestModelId: number; manual?: boolean }
@@ -250,6 +250,7 @@ const HFModelFile: React.FC<HFModelFileProps> = forwardRef((props, ref) => {
}
try {
const evaluateFileList = list.map((item: any) => {
// TODO: llamabox is remove from backend options, user should select a available backend
return {
backend: backendOptionsMap.llamaBox,
source: modelSource,
@@ -363,7 +364,7 @@ const HFModelFile: React.FC<HFModelFileProps> = forwardRef((props, ref) => {
return (
<div className="files-wrap">
<TitleWrapper>
<TitleWrapper style={{ paddingInline: '24px' }}>
<span className="title">
{intl.formatMessage({ id: 'models.available.files' })} (
{dataSource.fileList.length || 0})
+2 -2
View File
@@ -311,7 +311,7 @@ const ModelCard: React.FC<{
return (
<>
<TitleWrapper>
<TitleWrapper style={{ paddingInline: 24 }}>
<div className="title">{modelData?.id || modelData?.name} </div>
{generateModelLink()}
</TitleWrapper>
@@ -392,7 +392,7 @@ const ModelCard: React.FC<{
<div style={{ minHeight: 200 }}>
{readmeText && (
<>
<TitleWrapper>
<TitleWrapper style={{ paddingInline: 24 }}>
<span className="title">README.md</span>
</TitleWrapper>
<div className="card-wrapper">
@@ -22,8 +22,8 @@ import SearchResult from './search-result';
const filterOptions = [
{ label: 'AWQ', value: 'awq' },
{ label: 'GPTQ', value: 'gptq' }
// { label: 'GGUF', value: 'gguf' }
{ label: 'GPTQ', value: 'gptq' },
{ label: 'GGUF', value: 'gguf' }
];
const PaginationMain = styled(Pagination)`
.ant-pagination-slash {
@@ -523,11 +523,6 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
onChange={handleSearchInputChange}
modelSource={modelSource}
></SearchInput>
{/* <div className="gguf-tips">
<span>
{intl.formatMessage({ id: 'models.form.search.gguftips' })}
</span>
</div> */}
<div className={SearchStyle.filter}>
<span className="flex-center gap-8">
<BaseSelect
@@ -1,9 +1,9 @@
import React from 'react';
import '../style/title-wrapper.less';
const TitleWrapper: React.FC<any> = ({ children }) => {
const TitleWrapper: React.FC<any> = ({ children, style }) => {
return (
<h3 className="h3" style={{ height: 50 }}>
<h3 className="h3" style={{ height: 50, ...style }}>
{children}
</h3>
);