chore: update creation form
This commit is contained in:
@@ -157,6 +157,7 @@ const SealCascader: React.FC<
|
||||
|
||||
const handleDropdownVisibleChange = (open: boolean) => {
|
||||
setVisible(open);
|
||||
props.onOpenChange?.(open);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -3,7 +3,7 @@ export default {
|
||||
'benchmark.button.add': 'Add Benchmark',
|
||||
'benchmark.button.compare': 'Compare',
|
||||
'benchmark.table.model': 'Model',
|
||||
'benchmark.table.instance': 'Endpoint',
|
||||
'benchmark.table.instance': 'Model Instance',
|
||||
'benchmark.table.dataset': 'Dataset',
|
||||
'benchmark.table.requestRate': 'Request Rate',
|
||||
'benchmark.table.gpu': 'GPU ',
|
||||
|
||||
@@ -15,6 +15,7 @@ export default {
|
||||
'menu.models.deployment': 'Deployments',
|
||||
'menu.models.userModels': 'My Models',
|
||||
'menu.models.benchmark': 'Benchmarks',
|
||||
'menu.models.benchmarkDetail': 'Benchmark Details',
|
||||
'menu.modelCatalog': 'Catalog',
|
||||
'menu.resources': 'Resources',
|
||||
'menu.apikeys': 'API Keys',
|
||||
|
||||
@@ -3,7 +3,7 @@ export default {
|
||||
'benchmark.button.add': 'Add Benchmark',
|
||||
'benchmark.button.compare': 'Compare',
|
||||
'benchmark.table.model': 'Model',
|
||||
'benchmark.table.instance': 'Endpoint',
|
||||
'benchmark.table.instance': 'Model Instance',
|
||||
'benchmark.table.dataset': 'Dataset',
|
||||
'benchmark.table.requestRate': 'Request Rate',
|
||||
'benchmark.table.gpu': 'GPU ',
|
||||
|
||||
@@ -14,6 +14,7 @@ export default {
|
||||
'menu.models.catalog': 'モデルカタログ',
|
||||
'menu.models.deployment': 'Deployment',
|
||||
'menu.models.benchmark': 'Benchmarks',
|
||||
'menu.models.benchmarkDetail': 'Benchmark Details',
|
||||
'menu.modelCatalog': 'カタログ',
|
||||
'menu.resources': 'リソース',
|
||||
'menu.apikeys': 'APIキー',
|
||||
|
||||
@@ -3,7 +3,7 @@ export default {
|
||||
'benchmark.button.add': 'Add Benchmark',
|
||||
'benchmark.button.compare': 'Compare',
|
||||
'benchmark.table.model': 'Model',
|
||||
'benchmark.table.instance': 'Endpoint',
|
||||
'benchmark.table.instance': 'Model Instance',
|
||||
'benchmark.table.dataset': 'Dataset',
|
||||
'benchmark.table.requestRate': 'Request Rate',
|
||||
'benchmark.table.gpu': 'GPU ',
|
||||
|
||||
@@ -14,6 +14,7 @@ export default {
|
||||
'menu.models.catalog': 'Каталог моделей',
|
||||
'menu.models.deployment': 'Запуск',
|
||||
'menu.models.benchmark': 'Benchmarks',
|
||||
'menu.models.benchmarkDetail': 'Benchmark Details',
|
||||
'menu.modelCatalog': 'Каталог',
|
||||
'menu.resources': 'Ресурсы',
|
||||
'menu.apikeys': 'API-ключи',
|
||||
@@ -39,4 +40,5 @@ export default {
|
||||
|
||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||
// 1. 'menu.models.benchmark': 'Benchmarks',
|
||||
// 2. 'menu.models.benchmarkDetail': 'Benchmark Details',
|
||||
// ========== End of To-Do List ==========
|
||||
|
||||
@@ -3,7 +3,7 @@ export default {
|
||||
'benchmark.button.add': '添加基准测试',
|
||||
'benchmark.button.compare': '对比',
|
||||
'benchmark.table.model': '模型',
|
||||
'benchmark.table.instance': '目标实例',
|
||||
'benchmark.table.instance': '模型实例',
|
||||
'benchmark.table.dataset': '数据集',
|
||||
'benchmark.table.requestRate': '请求率',
|
||||
'benchmark.table.gpu': 'GPU ',
|
||||
|
||||
@@ -14,6 +14,7 @@ export default {
|
||||
'menu.models.deployment': '部署',
|
||||
'menu.models.userModels': '我的模型',
|
||||
'menu.models.benchmark': '基准测试',
|
||||
'menu.models.benchmarkDetail': '基准测试详情',
|
||||
'menu.modelCatalog': '模型库',
|
||||
'menu.models.catalog': '模型库',
|
||||
'menu.resources': '资源',
|
||||
|
||||
@@ -2,6 +2,7 @@ import BaseSelect from '@/components/seal-form/base/select';
|
||||
import { SearchOutlined, SyncOutlined } from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button, Input, Space } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import React from 'react';
|
||||
|
||||
export interface RightActionsProps {
|
||||
@@ -9,8 +10,7 @@ export interface RightActionsProps {
|
||||
handleSearch: () => void;
|
||||
handleQueryChange: (value: any, option?: any) => void;
|
||||
modelList?: Global.BaseOption<number>[];
|
||||
datasetList?: Global.BaseOption<string>[];
|
||||
gpuVendorList?: Global.BaseOption<string>[];
|
||||
datasetList?: Global.BaseOption<string | number>[];
|
||||
}
|
||||
|
||||
const RightActions: React.FC<RightActionsProps> = ({
|
||||
@@ -18,11 +18,19 @@ const RightActions: React.FC<RightActionsProps> = ({
|
||||
handleSearch,
|
||||
handleQueryChange,
|
||||
modelList,
|
||||
datasetList,
|
||||
gpuVendorList
|
||||
datasetList
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const debounceUpdateFilter = _.debounce((e: any) => {
|
||||
handleQueryChange({
|
||||
page: 1,
|
||||
gpu_summary: e.target.value
|
||||
});
|
||||
}, 350);
|
||||
|
||||
const handleGPUChange = debounceUpdateFilter;
|
||||
|
||||
return (
|
||||
<Space>
|
||||
<Input
|
||||
@@ -34,14 +42,25 @@ const RightActions: React.FC<RightActionsProps> = ({
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'common.filter.name'
|
||||
})}
|
||||
style={{ width: 230 }}
|
||||
style={{ width: 180 }}
|
||||
allowClear
|
||||
onChange={handleInputChange}
|
||||
></Input>
|
||||
<Input
|
||||
prefix={
|
||||
<SearchOutlined
|
||||
style={{ color: 'var(--ant-color-text-placeholder)' }}
|
||||
></SearchOutlined>
|
||||
}
|
||||
placeholder="Filter by GPU"
|
||||
style={{ width: 180 }}
|
||||
allowClear
|
||||
onChange={handleGPUChange}
|
||||
></Input>
|
||||
<BaseSelect
|
||||
allowClear
|
||||
placeholder="Filter by model"
|
||||
style={{ width: 150 }}
|
||||
style={{ width: 200 }}
|
||||
options={modelList}
|
||||
onChange={(value, option) =>
|
||||
handleQueryChange({
|
||||
@@ -53,8 +72,12 @@ const RightActions: React.FC<RightActionsProps> = ({
|
||||
<BaseSelect
|
||||
allowClear
|
||||
placeholder="Filter by dataset"
|
||||
style={{ width: 150 }}
|
||||
options={datasetList}
|
||||
style={{ width: 200 }}
|
||||
options={datasetList?.map((item) => ({
|
||||
...item,
|
||||
label: item.label,
|
||||
value: item.label
|
||||
}))}
|
||||
onChange={(value, option) =>
|
||||
handleQueryChange({
|
||||
dataset_name: value,
|
||||
@@ -62,18 +85,6 @@ const RightActions: React.FC<RightActionsProps> = ({
|
||||
})
|
||||
}
|
||||
></BaseSelect>
|
||||
<BaseSelect
|
||||
allowClear
|
||||
placeholder="Filter by GPU vendor"
|
||||
onChange={(value, option) =>
|
||||
handleQueryChange({
|
||||
gpu_summary: value,
|
||||
page: 1
|
||||
})
|
||||
}
|
||||
style={{ width: 180 }}
|
||||
options={gpuVendorList}
|
||||
></BaseSelect>
|
||||
<Button
|
||||
type="text"
|
||||
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
||||
|
||||
@@ -81,20 +81,18 @@ export interface FormData {
|
||||
dataset_output_tokens: number;
|
||||
total_requests: number;
|
||||
request_rate: number;
|
||||
instance_snapshots: Record<string, InstanceSnapshot>;
|
||||
gpu_snapshots: Record<string, GPUSnapshot>;
|
||||
state: string;
|
||||
state_message: string;
|
||||
worker_id: number;
|
||||
gpu_summary: string;
|
||||
seed: number;
|
||||
gpu_vendor_summary: string;
|
||||
model_instance?: string;
|
||||
}
|
||||
|
||||
export interface BenchmarkListItem extends FormData {
|
||||
id: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
state: string;
|
||||
state_message: string;
|
||||
instance_snapshot: InstanceSnapshot;
|
||||
gpu_snapshot: GPUSnapshot[];
|
||||
}
|
||||
|
||||
export interface DatasetListItem {
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -9,10 +9,9 @@ import { useIntl, useNavigate } from '@umijs/max';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { ConfigProvider, Table, message } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import NoResult from '../_components/no-result';
|
||||
import PageBox from '../_components/page-box';
|
||||
import useQueryGPUs from '../resources/services/use-query-gpus';
|
||||
import {
|
||||
createBenchmark,
|
||||
deleteBenchmark,
|
||||
@@ -20,7 +19,6 @@ import {
|
||||
updateBenchmark
|
||||
} from './apis';
|
||||
import AddBenchmarkModal from './components/add-benchmark-modal';
|
||||
import DetailModal from './components/detail-modal';
|
||||
import LeftActions from './components/left-actions';
|
||||
import RightActions from './components/right-actions';
|
||||
import { FormData, BenchmarkListItem as ListItem } from './config/types';
|
||||
@@ -28,7 +26,7 @@ import useBenchmarkColumns from './hooks/use-benchmark-columns';
|
||||
import useColumnSettings from './hooks/use-column-settings';
|
||||
import useCreateBenchmark from './hooks/use-create-benchmark';
|
||||
import useExportData from './hooks/use-export-data';
|
||||
import useViewDetail from './hooks/use-view-detail';
|
||||
import useQueryDataset from './services/use-query-dataset';
|
||||
|
||||
const Benchmark: React.FC = () => {
|
||||
const {
|
||||
@@ -54,30 +52,16 @@ const Benchmark: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const { openBenchmarkModal, closeBenchmarkModal, openBenchmarkModalStatus } =
|
||||
useCreateBenchmark();
|
||||
const {
|
||||
openViewDetailModal,
|
||||
closeViewDetailModal,
|
||||
openViewDetailModalStatus
|
||||
} = useViewDetail();
|
||||
const { dataList: modelList, fetchData: fetchModelList } = useQueryModelList({
|
||||
getValue: (item: any) => item.name
|
||||
});
|
||||
const { fetchData: fetchGpuList } = useQueryGPUs();
|
||||
const { SettingsButton, selectedColumns } = useColumnSettings();
|
||||
const [gpuVendorList, setGpuVendorList] = useState<
|
||||
Global.BaseOption<string>[]
|
||||
>([]);
|
||||
|
||||
const { datasetList, fetchDatasetData } = useQueryDataset();
|
||||
|
||||
useEffect(() => {
|
||||
fetchModelList({ page: -1 });
|
||||
fetchGpuList({ page: -1 }).then((list) => {
|
||||
const vendors = _.uniq(list.map((gpu) => gpu.vendor).filter((v) => !!v));
|
||||
const vendorOptions = vendors.map((vendor: string) => ({
|
||||
label: vendor,
|
||||
value: vendor
|
||||
}));
|
||||
setGpuVendorList(vendorOptions);
|
||||
});
|
||||
fetchDatasetData();
|
||||
}, []);
|
||||
|
||||
const handleAddBenchmark = () => {
|
||||
@@ -184,7 +168,7 @@ const Benchmark: React.FC = () => {
|
||||
left={
|
||||
<LeftActions
|
||||
modelList={modelList}
|
||||
gpuVendorList={gpuVendorList}
|
||||
datasetList={datasetList}
|
||||
handleSearch={handleSearch}
|
||||
handleQueryChange={handleQueryChange}
|
||||
handleInputChange={handleNameChange}
|
||||
@@ -234,11 +218,6 @@ const Benchmark: React.FC = () => {
|
||||
onCancel={handleModalCancel}
|
||||
onOk={handleModalOk}
|
||||
></AddBenchmarkModal>
|
||||
<DetailModal
|
||||
open={openViewDetailModalStatus.open}
|
||||
currentData={openViewDetailModalStatus.currentData}
|
||||
onClose={closeViewDetailModal}
|
||||
></DetailModal>
|
||||
<DeleteModal ref={modalRef}></DeleteModal>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { useQueryDataList } from '@/hooks/use-query-data-list';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { useState } from 'react';
|
||||
import { queryDatasetList } from '../apis';
|
||||
import { DatasetListItem } from '../config/types';
|
||||
|
||||
const useQueryDataset = () => {
|
||||
const intl = useIntl();
|
||||
const { dataList, loading, fetchData, cancelRequest } = useQueryDataList<
|
||||
DatasetListItem,
|
||||
Global.SearchParams
|
||||
@@ -30,7 +32,7 @@ const useQueryDataset = () => {
|
||||
setDatasetList([
|
||||
...list,
|
||||
{
|
||||
label: 'Custom',
|
||||
label: intl.formatMessage({ id: 'backend.custom' }),
|
||||
value: 'Custom'
|
||||
}
|
||||
]);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useQueryData } from '@/hooks/use-query-data-list';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import _ from 'lodash';
|
||||
import { useState } from 'react';
|
||||
import { queryProfiles } from '../apis';
|
||||
@@ -11,6 +12,7 @@ export default function useQueryProfiles() {
|
||||
fetchDetail: queryProfiles,
|
||||
key: 'profiles'
|
||||
});
|
||||
const intl = useIntl();
|
||||
const [profilesOptions, setProfilesOptions] = useState<
|
||||
{
|
||||
label: string;
|
||||
@@ -35,7 +37,7 @@ export default function useQueryProfiles() {
|
||||
setProfilesOptions([
|
||||
...list,
|
||||
{
|
||||
label: 'Custom',
|
||||
label: intl.formatMessage({ id: 'backend.custom' }),
|
||||
value: 'Custom',
|
||||
config: {
|
||||
dataset_name: '',
|
||||
|
||||
Reference in New Issue
Block a user