chore: clear space onblur

This commit is contained in:
jialin
2025-11-03 10:14:30 +08:00
parent 1a92cc8543
commit 303429c52c
7 changed files with 309 additions and 195 deletions
@@ -3,7 +3,6 @@ import SealSelect from '@/components/seal-form/seal-select';
import { PageAction } from '@/config';
import { PageActionType } from '@/config/types';
import useAppUtils from '@/hooks/use-app-utils';
import { LoadingOutlined } from '@ant-design/icons';
import { Link, useIntl } from '@umijs/max';
import { Form } from 'antd';
import { useAtom } from 'jotai';
@@ -13,13 +12,6 @@ import styled from 'styled-components';
import { ClusterFormData as FormData } from '../config/types';
import { useProviderRegions } from '../hooks/use-provider-regions';
type OptionData = {
label: string;
datacenter: string;
value: string | number;
icon: string;
};
const OptionItem = styled.div`
display: flex;
align-items: center;
@@ -42,13 +34,6 @@ const OptionItem = styled.div`
}
`;
const NoContent = styled.div`
display: flex;
justify-content: center;
align-items: center;
padding-block: 12px;
`;
interface CloudProviderProps {
provider: string; // 'kubernetes' | 'digitalocean';
credentialList: Global.BaseOption<number>[];
@@ -56,22 +41,6 @@ interface CloudProviderProps {
credentialID?: number;
}
const NotFoundContent: React.FC<{ loading: boolean }> = ({ loading }) => {
const intl = useIntl();
if (loading) {
return (
<NoContent>
<LoadingOutlined />
</NoContent>
);
}
return (
<NoContent>
{intl.formatMessage({ id: 'clusters.create.noRegions' })}
</NoContent>
);
};
const NotFoundCredentialContent: React.FC = () => {
const [, setFromClusterCreation] = useAtom(fromClusterCreationAtom);
const intl = useIntl();
@@ -81,11 +50,9 @@ const NotFoundCredentialContent: React.FC = () => {
};
return (
<NoContent>
<Link to={'/cluster-management/credentials'} onClick={handleOnClick}>
{intl.formatMessage({ id: 'clusters.button.addCredential' })}
</Link>
</NoContent>
<Link to={'/cluster-management/credentials'} onClick={handleOnClick}>
{intl.formatMessage({ id: 'clusters.button.addCredential' })}
</Link>
);
};
@@ -204,7 +171,9 @@ const CloudProvider: React.FC<CloudProviderProps> = (props) => {
labelRender={labelRender}
optionRender={optionRender}
onChange={handleRegionChange}
notFoundContent={<NotFoundContent loading={loading} />}
notFoundContent={intl.formatMessage({
id: 'clusters.create.noRegions'
})}
></SealSelect>
</Form.Item>
</>
+18 -151
View File
@@ -4,19 +4,12 @@ import SealInputNumber from '@/components/seal-form/input-number';
import SealInput from '@/components/seal-form/seal-input';
import SealSelect from '@/components/seal-form/seal-select';
import useAppUtils from '@/hooks/use-app-utils';
import useDeferredRequest from '@/hooks/use-deferred-request';
import { useIntl } from '@umijs/max';
import { Form } from 'antd';
import _ from 'lodash';
import { useEffect, useRef, useState } from 'react';
import {
queryDraftModelList,
queryHuggingfaceModels,
queryModelScopeModels
} from '../apis';
import { modelSourceMap } from '../config';
import { useRef } from 'react';
import { useFormContext } from '../config/form-context';
import { FormData } from '../config/types';
import useQueryDraftModels from '../hooks/use-query-draftModels';
const AlgorithmMap = {
Eagle3: 'eagle3',
@@ -34,144 +27,12 @@ const SpeculativeDecode = () => {
form
);
const algorithm = Form.useWatch(['speculative_config', 'algorithm'], form);
const [draftModelList, setDraftModelList] = useState<
Global.BaseOption<string, { options: Global.BaseOption<string>[] }>[]
>([]);
const presetDraftModelListRef = useRef<Global.BaseOption<string>[]>([]);
const speculativeConfigRef = useRef<any>({});
const axiosTokenRef = useRef<AbortController | null>(null);
const [loading, setLoading] = useState(false);
const fetchDraftModels = async () => {
const response = await queryDraftModelList({
page: 1,
perPage: 100
const { draftModelList, loading, resetDraftModels, onSearch } =
useQueryDraftModels({
source
});
const options = response.items.map((item) => ({
label: item.name,
value: item.name
}));
presetDraftModelListRef.current = options;
setDraftModelList(options);
};
const getHuggingfaceModels = async (query: string) => {
if (axiosTokenRef.current) {
axiosTokenRef.current.abort();
}
axiosTokenRef.current = new AbortController();
try {
const params = {
limit: 10,
search: {
query: query
}
};
setLoading(true);
const data = await queryHuggingfaceModels(params, {
signal: axiosTokenRef.current.signal
});
const list = _.map(data || [], (item: any) => {
return {
value: item.name,
label: item.name
};
});
const catalogModelList =
presetDraftModelListRef.current.length > 0
? [
{
label: `${intl.formatMessage({ id: 'models.form.source' })}: ${intl.formatMessage({ id: 'menu.models.modelCatalog' })}`,
title: `${intl.formatMessage({ id: 'models.form.source' })}: ${intl.formatMessage({ id: 'menu.models.modelCatalog' })}`,
options: presetDraftModelListRef.current || []
}
]
: [];
setDraftModelList([
...catalogModelList,
{
label: `${intl.formatMessage({ id: 'models.form.source' })}: Hugging Face`,
title: `${intl.formatMessage({ id: 'models.form.source' })}: Hugging Face`,
options: list
}
]);
} catch (error) {
setDraftModelList(presetDraftModelListRef.current);
} finally {
setLoading(false);
}
};
const getModelScopeModels = async (query: string) => {
if (axiosTokenRef.current) {
axiosTokenRef.current.abort();
}
axiosTokenRef.current = new AbortController();
try {
const params = {
Name: query,
PageSize: 10,
PageNumber: 1,
tasks: []
};
setLoading(true);
const data = await queryModelScopeModels(params, {
signal: axiosTokenRef.current.signal
});
const list = _.map(
_.get(data, 'Data.Model.Models') || [],
(item: any) => {
return {
label: `${item.Path}/${item.Name}`,
value: `${item.Path}/${item.Name}`
};
}
);
const catalogModelList =
presetDraftModelListRef.current.length > 0
? [
{
label: `${intl.formatMessage({ id: 'models.form.source' })}: ${intl.formatMessage({ id: 'menu.models.modelCatalog' })}`,
title: `${intl.formatMessage({ id: 'models.form.source' })}: ${intl.formatMessage({ id: 'menu.models.modelCatalog' })}`,
options: presetDraftModelListRef.current || []
}
]
: [];
setDraftModelList([
...catalogModelList,
{
label: `${intl.formatMessage({ id: 'models.form.source' })}: ModelScope`,
title: `${intl.formatMessage({ id: 'models.form.source' })}: ModelScope`,
options: list
}
]);
} catch (error) {
setDraftModelList(presetDraftModelListRef.current);
} finally {
setLoading(false);
}
};
const handleOnSearch = async (value: string) => {
if (!value) {
setDraftModelList(presetDraftModelListRef.current);
return;
}
if (source === modelSourceMap.huggingface_value) {
await getHuggingfaceModels(value);
} else if (source === modelSourceMap.modelscope_value) {
await getModelScopeModels(value);
}
};
const { run: onSearch } = useDeferredRequest(
(value: string) => handleOnSearch(value),
150
);
const handleSpeculativeEnabledChange = (e: any) => {
if (e.target.checked) {
@@ -191,11 +52,11 @@ const SpeculativeDecode = () => {
}
};
useEffect(() => {
if (algorithm === AlgorithmMap.Eagle3) {
fetchDraftModels();
const handleAlgorithemChange = (value: string) => {
if (value === AlgorithmMap.Eagle3) {
resetDraftModels();
}
}, [algorithm]);
};
return (
<>
@@ -228,11 +89,14 @@ const SpeculativeDecode = () => {
>
<SealSelect
required
onChange={handleAlgorithemChange}
label={intl.formatMessage({ id: 'models.form.algorithm' })}
options={[
{ label: 'Eagle3', value: AlgorithmMap.Eagle3 },
{ label: 'MTP', value: AlgorithmMap.MTP },
{ label: 'N-gram', value: AlgorithmMap.Ngram }
[
{ label: 'Eagle3', value: AlgorithmMap.Eagle3 },
{ label: 'MTP', value: AlgorithmMap.MTP },
{ label: 'N-gram', value: AlgorithmMap.Ngram }
]
]}
></SealSelect>
</Form.Item>
@@ -252,6 +116,9 @@ const SpeculativeDecode = () => {
<AutoComlete
required
allowClear
loading={loading}
trim={false}
clearSpaceOnBlur={true}
label={intl.formatMessage({ id: 'models.form.draftModel' })}
placeholder={intl.formatMessage({
id: 'models.form.draftModel.placeholder'
@@ -0,0 +1,193 @@
import { useIntl } from '@umijs/max';
import { useRequest } from 'ahooks';
import _ from 'lodash';
import { useEffect, useRef, useState } from 'react';
import {
queryDraftModelList,
queryHuggingfaceModels,
queryModelScopeModels
} from '../apis';
import { modelSourceMap } from '../config';
export default function useQueryDraftModels({ source }: { source: string }) {
const intl = useIntl();
const presetDraftModelListRef = useRef<Global.BaseOptionGroup<string>[]>([]);
const axiosTokenRef = useRef<AbortController | null>(null);
const [loading, setLoading] = useState(false);
const [draftModelList, setDraftModelList] = useState<
Global.BaseOptionGroup<string>[]
>([]);
const fetchDraftModels = async () => {
const response = await queryDraftModelList({
page: 1,
perPage: 100
});
const options = response.items.map((item) => ({
label: item.name,
value: item.name
}));
presetDraftModelListRef.current = options;
setDraftModelList(options);
};
const generateSourceLabel = (source: string) => {
let label = '';
const sourceLabel = intl.formatMessage({ id: 'models.form.source' });
if (source === modelSourceMap.huggingface_value) {
label = `${sourceLabel}: Hugging Face`;
}
if (source === modelSourceMap.modelscope_value) {
label = `${sourceLabel}: ModelScope`;
}
if (source === 'catalog') {
label = `${sourceLabel}: ${intl.formatMessage({ id: 'menu.models.modelCatalog' })}`;
}
return {
label: label
};
};
const getHuggingfaceModels = async (query: string) => {
if (axiosTokenRef.current) {
axiosTokenRef.current.abort();
}
axiosTokenRef.current = new AbortController();
try {
const params = {
limit: 10,
search: {
query: query
}
};
setLoading(true);
const data = await queryHuggingfaceModels(params, {
signal: axiosTokenRef.current.signal
});
const list = _.map(data || [], (item: any) => {
return {
value: item.name,
label: item.name
};
});
const catalogModelList =
presetDraftModelListRef.current.length > 0
? [
{
...generateSourceLabel('catalog'),
options: presetDraftModelListRef.current || []
}
]
: [];
setDraftModelList([
...catalogModelList,
{
...generateSourceLabel(modelSourceMap.huggingface_value),
options: list
}
]);
} catch (error) {
setDraftModelList(presetDraftModelListRef.current);
} finally {
setLoading(false);
}
};
const getModelScopeModels = async (query: string) => {
if (axiosTokenRef.current) {
axiosTokenRef.current.abort();
}
axiosTokenRef.current = new AbortController();
try {
const params = {
Name: query,
PageSize: 10,
PageNumber: 1,
tasks: []
};
setLoading(true);
const data = await queryModelScopeModels(params, {
signal: axiosTokenRef.current.signal
});
const list = _.map(
_.get(data, 'Data.Model.Models') || [],
(item: any) => {
return {
label: `${item.Path}/${item.Name}`,
value: `${item.Path}/${item.Name}`
};
}
);
const catalogModelList =
presetDraftModelListRef.current.length > 0
? [
{
...generateSourceLabel('catalog'),
options: presetDraftModelListRef.current || []
}
]
: [];
setDraftModelList([
...catalogModelList,
{
...generateSourceLabel(modelSourceMap.modelscope_value),
options: list
}
]);
} catch (error) {
setDraftModelList(presetDraftModelListRef.current);
} finally {
setLoading(false);
}
};
const handleOnSearch = async (value: string) => {
if (!value) {
setDraftModelList(presetDraftModelListRef.current);
return;
}
if (source === modelSourceMap.huggingface_value) {
await getHuggingfaceModels(value);
} else if (source === modelSourceMap.modelscope_value) {
await getModelScopeModels(value);
}
};
const { run: onSearch, cancel: cancelSearch } = useRequest(
(value: string) => handleOnSearch(value),
{
manual: true,
debounceWait: 300
}
);
const resetDraftModels = () => {
setDraftModelList(presetDraftModelListRef.current);
};
useEffect(() => {
fetchDraftModels();
return () => {
if (axiosTokenRef.current) {
axiosTokenRef.current.abort();
}
cancelSearch();
};
}, []);
return {
draftModelList,
loading,
fetchDraftModels,
onSearch,
resetDraftModels
};
}