chore: update creation form

This commit is contained in:
jialin
2026-01-30 18:48:55 +08:00
parent 3fe63f836f
commit 6c42b87c57
19 changed files with 294 additions and 251 deletions
+6 -93
View File
@@ -4,17 +4,12 @@ import { PageAction } from '@/config';
import useAppUtils from '@/hooks/use-app-utils';
import { ClusterStatusValueMap } from '@/pages/cluster-management/config';
import { useQueryClusterList } from '@/pages/cluster-management/services/use-query-cluster-list';
import {
InstanceStatusMap,
InstanceStatusMapValue
} from '@/pages/llmodels/config';
import { useQueryModelInstancesList } from '@/pages/llmodels/services/use-query-model-instances';
import { useQueryModelList } from '@/pages/llmodels/services/use-query-model-list';
import { useIntl } from '@umijs/max';
import { Form } from 'antd';
import React, { useEffect } from 'react';
import { useFormContext } from '../config/form-context';
import { FormData } from '../config/types';
import ModelInstanceForm from './model-instance';
const BasicForm: React.FC = () => {
const intl = useIntl();
@@ -27,49 +22,6 @@ const BasicForm: React.FC = () => {
cancelRequest: cancelClusterRequest,
clusterList
} = useQueryClusterList();
const {
loading: modelLoading,
fetchData: fetchModelList,
cancelRequest: cancelModelRequest,
dataList: modelList
} = useQueryModelList();
const {
loading: instanceLoading,
fetchInstanceList,
cancelRequest: cancelInstanceRequest,
instanceList
} = useQueryModelInstancesList();
const onModelListOpenChange = async (open: boolean) => {
if (open && modelList.length === 0) {
await form.validateFields(['cluster_id']);
const cluster_id = form.getFieldValue('cluster_id');
if (cluster_id) {
fetchModelList({ page: -1, cluster_id });
}
}
};
const onInstanceOpenChange = async (open: boolean) => {
if (open && instanceList.length === 0) {
const model_id = form.getFieldValue('model_id');
await form.validateFields(['model_name']);
if (model_id) fetchInstanceList({ id: model_id });
}
};
const handleOnModelChange = (value: string, option: any) => {
form.setFieldValue('model_id', option?.id);
};
const optionRender = (option: any) => {
return (
<span className="flex-center">
{option.label}
<span className="text-tertiary m-l-4">{`[${InstanceStatusMapValue[option.data?.state]}]`}</span>
</span>
);
};
useEffect(() => {
const initClusterId = (list: any[]) => {
@@ -95,8 +47,6 @@ const BasicForm: React.FC = () => {
useEffect(() => {
if (!open) {
cancelClusterRequest();
cancelModelRequest();
cancelInstanceRequest();
}
}, [open]);
@@ -133,53 +83,16 @@ const BasicForm: React.FC = () => {
required
></SealSelect>
</Form.Item>
<Form.Item<FormData>
name="model_name"
rules={[
{
required: true,
message: getRuleMessage('select', 'benchmark.table.model')
}
]}
>
<SealSelect
loading={modelLoading}
options={modelList.map((item) => ({
...item,
label: item.name,
value: item.name
}))}
onOpenChange={onModelListOpenChange}
onChange={handleOnModelChange}
label={intl.formatMessage({ id: 'benchmark.table.model' })}
required
></SealSelect>
<Form.Item<FormData> name="model_name" hidden={true}>
<SealInput.Input></SealInput.Input>
</Form.Item>
<Form.Item<FormData> name="model_id" hidden={true}>
<SealInput.Input></SealInput.Input>
</Form.Item>
<Form.Item<FormData>
name="model_instance_name"
rules={[
{
required: true,
message: getRuleMessage('select', 'benchmark.table.instance')
}
]}
>
<SealSelect
loading={instanceLoading}
options={instanceList.map((item) => ({
...item,
disabled: item.state !== InstanceStatusMap.Running
}))}
optionRender={optionRender}
onOpenChange={onInstanceOpenChange}
label={intl.formatMessage({ id: 'benchmark.table.instance' })}
required
></SealSelect>
<Form.Item<FormData> name="model_instance_name" hidden={true}>
<SealInput.Input></SealInput.Input>
</Form.Item>
<ModelInstanceForm></ModelInstanceForm>
<Form.Item<FormData> name="description">
<SealInput.TextArea
scaleSize={true}
+7 -90
View File
@@ -1,5 +1,4 @@
import AutoComplete from '@/components/seal-form/auto-complete';
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';
@@ -14,18 +13,12 @@ import useQueryDataset from '../services/use-query-dataset';
import useQueryProfiles from '../services/use-query-profiles';
import RandomSettingsForm from './random-settings';
const profileFields = [
'dataset_prompt_tokens',
'dataset_output_tokens',
'request_rate',
'total_requests'
];
const DatasetForm: React.FC = () => {
const intl = useIntl();
const form = Form.useFormInstance();
const { getRuleMessage } = useAppUtils();
const { action, open } = useFormContext();
const profile = Form.useWatch('profile', form);
const {
datasetList,
loading: datasetLoading,
@@ -37,17 +30,12 @@ const DatasetForm: React.FC = () => {
fetchProfilesData,
cancelRequest: cancelProfilesRequest
} = useQueryProfiles();
const profileConfigCache = React.useRef<{ [key: string]: any }>({});
const handleOnDataSetChange = (value: any, option: any) => {
if (value === 'Custom') {
form.setFieldsValue({
profile: ProfileValueMap.Custom,
dataset_id: null,
dataset_prompt_tokens: null,
dataset_output_tokens: null,
request_rate: null,
total_requests: null
dataset_id: null
});
} else {
form.setFieldsValue({
@@ -61,21 +49,12 @@ const DatasetForm: React.FC = () => {
}
};
const setCustomProfileValues = () => {
form.setFieldsValue({
dataset_name: 'Custom',
dataset_id: null,
dataset_prompt_tokens: null,
dataset_output_tokens: null,
request_rate: null,
total_requests: null
});
};
const handleProfileChange = (value: string, option: any) => {
if (value === ProfileValueMap.Custom) {
setCustomProfileValues();
profileConfigCache.current = {};
form.setFieldsValue({
dataset_name: 'Custom',
dataset_id: null
});
} else {
const dataset_id = datasetList.find(
(item) => item.label === option.config?.dataset_name
@@ -85,37 +64,9 @@ const DatasetForm: React.FC = () => {
dataset_id: dataset_id,
..._.omit(option?.config, ['description', 'dataset_source'])
});
profileConfigCache.current = {
profile: value,
config: {
dataset_id: dataset_id,
..._.omit(option?.config, ['description', 'dataset_source'])
}
};
}
};
const handleOnProfileConfigChange = async () => {
const values = form.getFieldsValue(profileFields);
if (
_.isEqual(values, {
..._.pick(profileConfigCache.current.config, profileFields)
})
) {
form.setFieldsValue({
profile: profileConfigCache.current.profile,
...profileConfigCache.current.config
});
return;
}
form.setFieldsValue({
profile: 'Custom',
dataset_name: 'Custom'
});
};
useEffect(() => {
if (!open) {
cancelDatasetRequest();
@@ -170,41 +121,7 @@ const DatasetForm: React.FC = () => {
<Form.Item<FormData> hidden name="dataset_id">
<SealInput.Input></SealInput.Input>
</Form.Item>
<RandomSettingsForm
onValueChange={handleOnProfileConfigChange}
></RandomSettingsForm>
<Form.Item<FormData>
name="request_rate"
rules={[
{
required: true,
message: getRuleMessage('input', 'benchmark.table.requestRate')
}
]}
>
<SealInputNumber
min={0}
onChange={handleOnProfileConfigChange}
label={intl.formatMessage({ id: 'benchmark.table.requestRate' })}
required
></SealInputNumber>
</Form.Item>
<Form.Item<FormData>
name="total_requests"
rules={[
{
required: true,
message: getRuleMessage('input', 'benchmark.form.totalRequests')
}
]}
>
<SealInputNumber
min={0}
onChange={handleOnProfileConfigChange}
label={intl.formatMessage({ id: 'benchmark.form.totalRequests' })}
required
></SealInputNumber>
</Form.Item>
{profile === 'Custom' && <RandomSettingsForm></RandomSettingsForm>}
</>
);
};
+26 -2
View File
@@ -17,6 +17,7 @@ import FormContext from '../config/form-context';
import { FormData, BenchmarkListItem as ListItem } from '../config/types';
import Basic from './basic';
import DatasetForm from './dataset';
import RandomSettingsForm from './random-settings';
interface ProviderFormProps {
ref?: any;
@@ -28,17 +29,20 @@ interface ProviderFormProps {
const TABKeysMap = {
BASIC: 'basic',
PROFILE: 'profile'
PROFILE: 'profile',
ADVANCED: 'advanced'
};
const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
const { action, currentData, onFinish, open } = props;
const intl = useIntl();
const [form] = Form.useForm();
const profile = Form.useWatch('profile', form);
const { getScrollElementScrollableHeight } = useWrapperContext();
const [activeKey, setActiveKey] = useState<string[]>([TABKeysMap.PROFILE]);
const scrollTabsRef = useRef<any>(null);
const showAdvanced = profile !== 'Custom' && Boolean(profile);
const segmentOptions = [
{
@@ -80,6 +84,25 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
}
}, [form, currentData, action]);
useEffect(() => {
if (!showAdvanced && activeKey?.includes(TABKeysMap.ADVANCED)) {
setActiveKey([TABKeysMap.PROFILE]);
}
}, [showAdvanced, activeKey]);
const advancedItems = showAdvanced
? [
{
key: TABKeysMap.ADVANCED,
label: intl.formatMessage({ id: 'resources.form.advanced' }),
forceRender: true,
children: <RandomSettingsForm />
}
]
: [];
console.log('render form with profile:', showAdvanced);
return (
<ScrollSpyTabs
ref={scrollTabsRef}
@@ -121,7 +144,8 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
label: intl.formatMessage({ id: 'common.title.config' }),
forceRender: true,
children: <DatasetForm />
}
},
...advancedItems
]}
></CollapsePanel>
</Form>
@@ -0,0 +1,157 @@
import SealCascader from '@/components/seal-form/seal-cascader';
import useAppUtils from '@/hooks/use-app-utils';
import {
InstanceStatusMap,
InstanceStatusMapValue
} from '@/pages/llmodels/config';
import { useQueryModelInstancesList } from '@/pages/llmodels/services/use-query-model-instances';
import { useQueryModelList } from '@/pages/llmodels/services/use-query-model-list';
import { useIntl } from '@umijs/max';
import { Form } from 'antd';
import React, { useEffect } from 'react';
import { useFormContext } from '../config/form-context';
import { FormData } from '../config/types';
const InstanceNode = (props: any) => {
const { data: instance } = props;
return instance.isLeaf ? (
<span className="flex-center">
{instance.label}
{instance.disabled && (
<span className="text-tertiary m-l-4">[{instance.state}]</span>
)}
</span>
) : (
<span>{instance.label}</span>
);
};
const ModelInstanceForm: React.FC = () => {
const intl = useIntl();
const form = Form.useFormInstance();
const { getRuleMessage } = useAppUtils();
const { action, open } = useFormContext();
const [modelList, setModelList] = React.useState<any[]>([]);
const {
loading: modelLoading,
fetchData: fetchModelList,
cancelRequest: cancelModelRequest
} = useQueryModelList();
const {
loading: instanceLoading,
fetchInstanceList,
cancelRequest: cancelInstanceRequest
} = useQueryModelInstancesList();
const handleOnChange = async (value: any, selectedOptions: any) => {
form.setFieldsValue({
model_name: value[0],
model_id: selectedOptions[0]?.id,
model_instance_name: value[1]
});
};
const renderInstance = (instance: any) => {
return {
label: instance.name,
value: instance.name,
id: instance.id,
isLeaf: true,
disabled: instance.state !== InstanceStatusMap.Running,
state: InstanceStatusMapValue[instance.state]
};
};
const loadInstances = async (selectedOptions: any[]) => {
const targetOption = selectedOptions[selectedOptions.length - 1];
if (targetOption && targetOption.children.length === 0) {
const list = await fetchInstanceList({ id: targetOption.id });
const instanceOptions = list.map((instance: any) =>
renderInstance(instance)
);
targetOption.children = [...instanceOptions];
setModelList((prevModelList) => {
const newModelList = prevModelList.map((model) => {
if (model.id === targetOption.id) {
return {
...model,
children: [...instanceOptions]
};
}
return model;
});
return newModelList;
});
}
};
const handleOnOpenChange = async (open: boolean) => {
// fetch model list when dropdown is opened
if (open && modelList.length === 0) {
const list = await fetchModelList({ page: -1 });
const modelOptions = list
.filter((model: any) => model.replicas > 0)
.map((model: any) => ({
label: model.name,
value: model.name,
id: model.id,
isLeaf: false,
children: []
}));
// preload instances for the first model
const instanceList = await fetchInstanceList({ id: modelOptions[0]?.id });
const instanceOptions = instanceList.map((instance: any) =>
renderInstance(instance)
);
if (modelOptions[0]) {
modelOptions[0].children = [...instanceOptions] as never[];
}
setModelList(modelOptions);
}
};
useEffect(() => {
if (!open) {
cancelModelRequest();
cancelInstanceRequest();
}
}, [open]);
return (
<Form.Item<FormData>
name={'model_instance'}
rules={[
{
required: true,
message: getRuleMessage('select', 'benchmark.table.instance')
}
]}
>
<SealCascader
required
loading={modelLoading || instanceLoading}
changeOnSelect={false}
expandTrigger="hover"
multiple={false}
classNames={{
popup: {
root: 'cascader-popup-wrapper gpu-selector'
}
}}
maxTagCount={1}
label={intl.formatMessage({ id: 'benchmark.table.instance' })}
options={modelList}
getPopupContainer={(triggerNode) => triggerNode.parentNode}
optionNode={InstanceNode}
loadData={loadInstances}
onOpenChange={handleOnOpenChange}
onChange={handleOnChange}
></SealCascader>
</Form.Item>
);
};
export default ModelInstanceForm;
+40 -6
View File
@@ -5,13 +5,14 @@ import { Form } from 'antd';
import React from 'react';
import { FormData } from '../config/types';
const DatasetForm: React.FC<{
onValueChange?: (value: any) => void;
}> = ({ onValueChange }) => {
const RandomSettingsForm: React.FC = () => {
const intl = useIntl();
const form = Form.useFormInstance();
const profile = Form.useWatch('profile', form);
const { getRuleMessage } = useAppUtils();
const disabled = profile !== 'Custom' && Boolean(profile);
return (
<>
<Form.Item<FormData>
@@ -25,9 +26,9 @@ const DatasetForm: React.FC<{
>
<SealInputNumber
min={0}
disabled={disabled}
label={intl.formatMessage({ id: 'benchmark.table.inputTokenLength' })}
required
onChange={onValueChange}
></SealInputNumber>
</Form.Item>
<Form.Item<FormData>
@@ -44,11 +45,11 @@ const DatasetForm: React.FC<{
>
<SealInputNumber
min={0}
disabled={disabled}
label={intl.formatMessage({
id: 'benchmark.table.outputTokenLength'
})}
required
onChange={onValueChange}
></SealInputNumber>
</Form.Item>
<Form.Item<FormData>
@@ -57,11 +58,44 @@ const DatasetForm: React.FC<{
>
<SealInputNumber
min={0}
disabled={disabled}
label={intl.formatMessage({ id: 'playground.image.params.seed' })}
></SealInputNumber>
</Form.Item>
<Form.Item<FormData>
name="request_rate"
rules={[
{
required: true,
message: getRuleMessage('input', 'benchmark.table.requestRate')
}
]}
>
<SealInputNumber
min={0}
disabled={disabled}
label={intl.formatMessage({ id: 'benchmark.table.requestRate' })}
required
></SealInputNumber>
</Form.Item>
<Form.Item<FormData>
name="total_requests"
rules={[
{
required: true,
message: getRuleMessage('input', 'benchmark.form.totalRequests')
}
]}
>
<SealInputNumber
min={0}
disabled={disabled}
label={intl.formatMessage({ id: 'benchmark.form.totalRequests' })}
required
></SealInputNumber>
</Form.Item>
</>
);
};
export default DatasetForm;
export default RandomSettingsForm;