feat: benchmark list
This commit is contained in:
@@ -2,7 +2,7 @@ import { createFromIconfontCN } from '@ant-design/icons';
|
||||
// import './iconfont/iconfont.js';
|
||||
|
||||
const IconFont = createFromIconfontCN({
|
||||
scriptUrl: '//at.alicdn.com/t/c/font_4613488_7f4klfqdzm.js'
|
||||
scriptUrl: '//at.alicdn.com/t/c/font_4613488_9wb9516md1k.js'
|
||||
});
|
||||
|
||||
export default IconFont;
|
||||
|
||||
@@ -112,7 +112,7 @@ export const FilterBar: React.FC<FilterBarProps> = (props) => {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Space size={20}>
|
||||
<Space size={16}>
|
||||
{handleClickPrimary ? (
|
||||
actionType === 'dropdown' ? (
|
||||
<DropDownActions
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
export default {
|
||||
'benchmark.title': 'Benchmark',
|
||||
'benchmark.button.add': 'Add Benchmark',
|
||||
'benchmark.button.compare': 'Compare',
|
||||
'benchmark.table.model': 'Model',
|
||||
'benchmark.table.dataset': 'Dataset',
|
||||
'benchmark.table.requestRate': 'Request Rate',
|
||||
'benchmark.table.gpu': 'GPU ',
|
||||
'benchmark.table.tpot': 'TPOT (ms)',
|
||||
'benchmark.table.itl': 'ITL (ms)',
|
||||
'benchmark.table.ttft': 'TTFT (ms)',
|
||||
'benchmark.table.rps': 'Requests/s',
|
||||
'benchmark.table.tps': 'Tokens/s',
|
||||
'benchmark.table.inputLen': 'Input Length',
|
||||
'benchmark.table.outputLen': 'Output Length'
|
||||
};
|
||||
@@ -14,6 +14,7 @@ export default {
|
||||
'menu.models.catalog': 'Model Catalog',
|
||||
'menu.models.deployment': 'Deployments',
|
||||
'menu.models.userModels': 'My Models',
|
||||
'menu.models.benchmark': 'Benchmark',
|
||||
'menu.modelCatalog': 'Catalog',
|
||||
'menu.resources': 'Resources',
|
||||
'menu.apikeys': 'API Keys',
|
||||
|
||||
@@ -39,5 +39,8 @@ export default {
|
||||
'noresult.resources.worker':
|
||||
'No workers available. Add a worker to get started.',
|
||||
'noresult.resources.gotocluster': 'Create Your First Cluster',
|
||||
'noresult.resources.gotoworker': 'Add Worker'
|
||||
'noresult.resources.gotoworker': 'Add Worker',
|
||||
'noresult.benchmark.title': 'No Benchmarks',
|
||||
'noresult.benchmark.subTitle': 'No benchmarks have been added yet.',
|
||||
'noresult.benchmark.nofound': 'No matching benchmarks found.'
|
||||
};
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
export default {
|
||||
'benchmark.title': 'Benchmark',
|
||||
'benchmark.button.add': 'Add Benchmark',
|
||||
'benchmark.button.compare': 'Compare',
|
||||
'benchmark.table.model': 'Model',
|
||||
'benchmark.table.dataset': 'Dataset',
|
||||
'benchmark.table.requestRate': 'Request Rate',
|
||||
'benchmark.table.gpu': 'GPU ',
|
||||
'benchmark.table.tpot': 'TPOT (ms)',
|
||||
'benchmark.table.itl': 'ITL (ms)',
|
||||
'benchmark.table.ttft': 'TTFT (ms)',
|
||||
'benchmark.table.rps': 'Requests/s',
|
||||
'benchmark.table.tps': 'Tokens/s',
|
||||
'benchmark.table.inputLen': 'Input Length',
|
||||
'benchmark.table.outputLen': 'Output Length'
|
||||
};
|
||||
@@ -13,6 +13,7 @@ export default {
|
||||
'menu.models.modelCatalog': 'カタログ',
|
||||
'menu.models.catalog': 'モデルカタログ',
|
||||
'menu.models.deployment': 'Deployment',
|
||||
'menu.models.benchmark': 'Benchmark',
|
||||
'menu.modelCatalog': 'カタログ',
|
||||
'menu.resources': 'リソース',
|
||||
'menu.apikeys': 'APIキー',
|
||||
@@ -50,5 +51,6 @@ export default {
|
||||
// 11. 'menu.models.userModels': 'My Models'
|
||||
// 12. 'menu.clusterManagement.clusterDetail': 'Cluster Detail',
|
||||
// 13. 'menu.clusterManagement.clusterCreate': 'Create Cluster',
|
||||
// 14. 'menu.resources.backendsList': 'Inference Backends'
|
||||
// 14. 'menu.resources.backendsList': 'Inference Backends',
|
||||
// 15. 'menu.models.benchmark': 'Benchmark',
|
||||
// ========== End of To-Do List ==========
|
||||
|
||||
@@ -39,5 +39,8 @@ export default {
|
||||
'noresult.resources.worker':
|
||||
'No workers available. Add a worker to get started.',
|
||||
'noresult.resources.gotocluster': 'Create Your First Cluster',
|
||||
'noresult.resources.gotoworker': 'Add Worker'
|
||||
'noresult.resources.gotoworker': 'Add Worker',
|
||||
'noresult.benchmark.title': 'No Benchmarks',
|
||||
'noresult.benchmark.subTitle': 'No benchmarks have been added yet.',
|
||||
'noresult.benchmark.nofound': 'No matching benchmarks found.'
|
||||
};
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
export default {
|
||||
'benchmark.title': 'Benchmark',
|
||||
'benchmark.button.add': 'Add Benchmark',
|
||||
'benchmark.button.compare': 'Compare',
|
||||
'benchmark.table.model': 'Model',
|
||||
'benchmark.table.dataset': 'Dataset',
|
||||
'benchmark.table.requestRate': 'Request Rate',
|
||||
'benchmark.table.gpu': 'GPU ',
|
||||
'benchmark.table.tpot': 'TPOT (ms)',
|
||||
'benchmark.table.itl': 'ITL (ms)',
|
||||
'benchmark.table.ttft': 'TTFT (ms)',
|
||||
'benchmark.table.rps': 'Requests/s',
|
||||
'benchmark.table.tps': 'Tokens/s',
|
||||
'benchmark.table.inputLen': 'Input Length',
|
||||
'benchmark.table.outputLen': 'Output Length'
|
||||
};
|
||||
@@ -13,6 +13,7 @@ export default {
|
||||
'menu.models.modelCatalog': 'Каталог',
|
||||
'menu.models.catalog': 'Каталог моделей',
|
||||
'menu.models.deployment': 'Запуск',
|
||||
'menu.models.benchmark': 'Benchmark',
|
||||
'menu.modelCatalog': 'Каталог',
|
||||
'menu.resources': 'Ресурсы',
|
||||
'menu.apikeys': 'API-ключи',
|
||||
@@ -37,5 +38,5 @@ export default {
|
||||
};
|
||||
|
||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||
|
||||
// 1. 'menu.models.benchmark': 'Benchmark',
|
||||
// ========== End of To-Do List ==========
|
||||
|
||||
@@ -40,12 +40,18 @@ export default {
|
||||
'noresult.resources.worker':
|
||||
'No workers available. Add a worker to get started.',
|
||||
'noresult.resources.gotocluster': 'Create Your First Cluster',
|
||||
'noresult.resources.gotoworker': 'Add Worker'
|
||||
'noresult.resources.gotoworker': 'Add Worker',
|
||||
'noresult.benchmark.title': 'No Benchmarks',
|
||||
'noresult.benchmark.subTitle': 'No benchmarks have been added yet.',
|
||||
'noresult.benchmark.nofound': 'No matching benchmarks found.'
|
||||
};
|
||||
|
||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||
// 1. 'noresult.resources.cluster': 'No clusters available. Add a cluster to get started.',
|
||||
// 2. 'noresult.resources.worker': 'No workers available. Add a worker to get started.',
|
||||
// 3. 'noresult.resources.gotocluster': 'Create Your First Cluster',
|
||||
// 4. 'noresult.resources.gotoworker': 'Add Worker'
|
||||
// 4. 'noresult.resources.gotoworker': 'Add Worker',
|
||||
// 5. 'noresult.benchmark.title': 'No Benchmarks',
|
||||
// 6. 'noresult.benchmark.subTitle': 'No benchmarks have been added yet.',
|
||||
// 7. 'noresult.benchmark.nofound': 'No matching benchmarks found.'
|
||||
// ========================================================================
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
export default {
|
||||
'benchmark.title': '基准测试',
|
||||
'benchmark.button.add': '添加基准测试',
|
||||
'benchmark.button.compare': '对比',
|
||||
'benchmark.table.model': '模型',
|
||||
'benchmark.table.dataset': '数据集',
|
||||
'benchmark.table.requestRate': '请求率',
|
||||
'benchmark.table.gpu': 'GPU ',
|
||||
'benchmark.table.tpot': 'TPOT (ms)',
|
||||
'benchmark.table.itl': 'ITL (ms)',
|
||||
'benchmark.table.ttft': 'TTFT (ms)',
|
||||
'benchmark.table.rps': '请求数/秒',
|
||||
'benchmark.table.tps': 'Tokens/秒',
|
||||
'benchmark.table.inputLen': '输入长度',
|
||||
'benchmark.table.outputLen': '输出长度'
|
||||
};
|
||||
@@ -13,6 +13,7 @@ export default {
|
||||
'menu.models.modelCatalog': '模型库',
|
||||
'menu.models.deployment': '部署',
|
||||
'menu.models.userModels': '我的模型',
|
||||
'menu.models.benchmark': '基准测试',
|
||||
'menu.modelCatalog': '模型库',
|
||||
'menu.models.catalog': '模型库',
|
||||
'menu.resources': '资源',
|
||||
|
||||
@@ -37,5 +37,8 @@ export default {
|
||||
'noresult.resources.cluster': '暂无可用集群,请添加集群以开始使用。',
|
||||
'noresult.resources.worker': '暂无可用节点,请添加节点以开始使用。',
|
||||
'noresult.resources.gotocluster': '创建您的第一个集群',
|
||||
'noresult.resources.gotoworker': '添加节点'
|
||||
'noresult.resources.gotoworker': '添加节点',
|
||||
'noresult.benchmark.title': '暂无基准测试',
|
||||
'noresult.benchmark.subTitle': '尚未添加任何基准测试。',
|
||||
'noresult.benchmark.nofound': '未找到匹配的基准测试'
|
||||
};
|
||||
|
||||
@@ -94,15 +94,17 @@ const ScrollSpyTabs: React.FC<ScrollSpyTabsProps> = forwardRef(
|
||||
|
||||
return (
|
||||
<div>
|
||||
<SegmentedHeader $top={segmentedTop.top}>
|
||||
<SegmentLine
|
||||
theme={'light'}
|
||||
defaultValue={target}
|
||||
value={target}
|
||||
onChange={handleTargetChange}
|
||||
options={segmentOptions}
|
||||
/>
|
||||
</SegmentedHeader>
|
||||
{segmentOptions.length > 0 && (
|
||||
<SegmentedHeader $top={segmentedTop.top}>
|
||||
<SegmentLine
|
||||
theme={'light'}
|
||||
defaultValue={target}
|
||||
value={target}
|
||||
onChange={handleTargetChange}
|
||||
options={segmentOptions}
|
||||
/>
|
||||
</SegmentedHeader>
|
||||
)}
|
||||
{children}
|
||||
<div className="holder" style={{ height: holderHeight }}></div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import { request } from '@umijs/max';
|
||||
import { CancelToken } from 'axios';
|
||||
import { BenchmarkListItem, FormData } from '../config/types';
|
||||
|
||||
export const BENCHMARKS_API = '/benchmark';
|
||||
export const DATASETS_API = '/datasets';
|
||||
|
||||
export async function queryBenchmarkList(
|
||||
params: Global.SearchParams,
|
||||
options?: {
|
||||
token?: CancelToken;
|
||||
}
|
||||
) {
|
||||
return request<Global.PageResponse<BenchmarkListItem>>(`${BENCHMARKS_API}`, {
|
||||
method: 'GET',
|
||||
params,
|
||||
cancelToken: options?.token
|
||||
});
|
||||
}
|
||||
|
||||
export async function createBenchmark(params: { data: FormData }) {
|
||||
return request(`${BENCHMARKS_API}`, {
|
||||
method: 'POST',
|
||||
data: params.data
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateBenchmark(params: { id: number; data: FormData }) {
|
||||
return request(`${BENCHMARKS_API}/${params.id}`, {
|
||||
method: 'PUT',
|
||||
data: params.data
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteBenchmark(id: number) {
|
||||
return request(`${BENCHMARKS_API}/${id}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
}
|
||||
|
||||
export async function queryBenchmarkLogs(
|
||||
id: number,
|
||||
options?: {
|
||||
token?: CancelToken;
|
||||
}
|
||||
) {
|
||||
return request(`${BENCHMARKS_API}/${id}/logs`, {
|
||||
method: 'GET',
|
||||
cancelToken: options?.token
|
||||
});
|
||||
}
|
||||
|
||||
export async function createBenchmarkResult(params: { id: number; data: any }) {
|
||||
return request(`${BENCHMARKS_API}/${params.id}/result`, {
|
||||
method: 'POST',
|
||||
data: params.data
|
||||
});
|
||||
}
|
||||
|
||||
export async function queryDatasetList(
|
||||
params: Global.SearchParams,
|
||||
options?: {
|
||||
token?: CancelToken;
|
||||
}
|
||||
) {
|
||||
return request<Global.PageResponse<BenchmarkListItem>>(`${DATASETS_API}`, {
|
||||
method: 'GET',
|
||||
params,
|
||||
cancelToken: options?.token
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { PageActionType } from '@/config/types';
|
||||
import FormDrawer from '@/pages/_components/form-drawer';
|
||||
import React, { useRef } from 'react';
|
||||
import { FormData, BenchmarkListItem as ListItem } from '../config/types';
|
||||
|
||||
import BenchmarkForm from '../forms';
|
||||
|
||||
type AddModalProps = {
|
||||
title: string;
|
||||
action: PageActionType;
|
||||
open: boolean;
|
||||
currentData?: ListItem; // Used when action is EDIT
|
||||
onOk: (values: FormData) => void;
|
||||
onCancel: () => void;
|
||||
};
|
||||
const AddBenchmark: React.FC<AddModalProps> = ({
|
||||
title,
|
||||
action,
|
||||
open,
|
||||
currentData,
|
||||
onOk,
|
||||
onCancel
|
||||
}) => {
|
||||
const form = useRef<any>(null);
|
||||
|
||||
const handleSubmit = () => {
|
||||
form.current?.submit();
|
||||
};
|
||||
|
||||
const handleOk = async (data: FormData) => {
|
||||
onOk({
|
||||
...data
|
||||
});
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
form.current?.resetFields();
|
||||
onCancel();
|
||||
};
|
||||
|
||||
return (
|
||||
<FormDrawer
|
||||
title={title}
|
||||
open={open}
|
||||
onCancel={handleCancel}
|
||||
onSubmit={handleSubmit}
|
||||
width={600}
|
||||
>
|
||||
<BenchmarkForm
|
||||
ref={form}
|
||||
action={action}
|
||||
currentData={currentData}
|
||||
onFinish={handleOk}
|
||||
/>
|
||||
</FormDrawer>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddBenchmark;
|
||||
@@ -0,0 +1,61 @@
|
||||
import {
|
||||
DeleteOutlined,
|
||||
PlusOutlined,
|
||||
SettingOutlined
|
||||
} from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button, Space } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
export interface RightActionsProps {
|
||||
handleDeleteByBatch: () => void;
|
||||
handleClickPrimary?: () => void;
|
||||
handleSettingFields?: () => void;
|
||||
handleCompare?: () => void;
|
||||
buttonText?: string;
|
||||
rowSelection: {
|
||||
selectedRowKeys: React.Key[];
|
||||
};
|
||||
}
|
||||
|
||||
const RightActions: React.FC<RightActionsProps> = ({
|
||||
handleDeleteByBatch,
|
||||
handleClickPrimary,
|
||||
handleCompare,
|
||||
handleSettingFields,
|
||||
buttonText,
|
||||
rowSelection
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<Space size={16}>
|
||||
<Button onClick={handleSettingFields} icon={<SettingOutlined />}></Button>
|
||||
<Button variant="solid" color="orange" onClick={handleCompare}>
|
||||
Compare
|
||||
</Button>
|
||||
<Button
|
||||
icon={<PlusOutlined></PlusOutlined>}
|
||||
type="primary"
|
||||
onClick={handleClickPrimary}
|
||||
>
|
||||
{buttonText}
|
||||
</Button>
|
||||
<Button
|
||||
icon={<DeleteOutlined />}
|
||||
danger
|
||||
onClick={handleDeleteByBatch}
|
||||
disabled={!rowSelection?.selectedRowKeys?.length}
|
||||
>
|
||||
<span>
|
||||
{intl?.formatMessage?.({ id: 'common.button.delete' })}
|
||||
{rowSelection?.selectedRowKeys?.length > 0 && (
|
||||
<span>({rowSelection?.selectedRowKeys?.length})</span>
|
||||
)}
|
||||
</span>
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
export default RightActions;
|
||||
@@ -0,0 +1,20 @@
|
||||
import { PageActionType } from '@/config/types';
|
||||
import { createContext, useContext } from 'react';
|
||||
|
||||
interface FormContextProps {
|
||||
action: PageActionType;
|
||||
}
|
||||
|
||||
const FormContext = createContext<FormContextProps>({} as FormContextProps);
|
||||
|
||||
export const useFormContext = () => {
|
||||
const context = useContext(FormContext);
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
'useFormContext must be used within a FormContext.Provider'
|
||||
);
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
export default FormContext;
|
||||
@@ -0,0 +1,29 @@
|
||||
import { StatusMaps } from '@/config';
|
||||
import { StatusType } from '@/config/types';
|
||||
|
||||
export const BenchmarkStatusValueMap = {
|
||||
Pending: 'pending',
|
||||
Claimed: 'claimed',
|
||||
Running: 'running',
|
||||
Completed: 'completed',
|
||||
Error: 'error',
|
||||
Unreachable: 'unreachable'
|
||||
};
|
||||
|
||||
export const BenchmarkStatusLabelMap = {
|
||||
[BenchmarkStatusValueMap.Pending]: 'Pending',
|
||||
[BenchmarkStatusValueMap.Claimed]: 'Claimed',
|
||||
[BenchmarkStatusValueMap.Running]: 'Running',
|
||||
[BenchmarkStatusValueMap.Completed]: 'Completed',
|
||||
[BenchmarkStatusValueMap.Error]: 'Error',
|
||||
[BenchmarkStatusValueMap.Unreachable]: 'Unreachable'
|
||||
};
|
||||
|
||||
export const BenchmarkStatus: Record<string, StatusType> = {
|
||||
[BenchmarkStatusValueMap.Pending]: StatusMaps.transitioning,
|
||||
[BenchmarkStatusValueMap.Claimed]: StatusMaps.warning,
|
||||
[BenchmarkStatusValueMap.Running]: StatusMaps.success,
|
||||
[BenchmarkStatusValueMap.Completed]: StatusMaps.success,
|
||||
[BenchmarkStatusValueMap.Error]: StatusMaps.error,
|
||||
[BenchmarkStatusValueMap.Unreachable]: StatusMaps.error
|
||||
};
|
||||
@@ -0,0 +1,103 @@
|
||||
export interface ComputedResourceClaim {
|
||||
is_unified_memory: boolean;
|
||||
offload_layers: number;
|
||||
total_layers: number;
|
||||
ram: number;
|
||||
vram: Record<string, number>;
|
||||
tensor_split: number[];
|
||||
vram_utilization: number;
|
||||
}
|
||||
|
||||
export interface ComputedResourceClaim1 {
|
||||
is_unified_memory: boolean;
|
||||
offload_layers: number;
|
||||
total_layers: number;
|
||||
ram: number;
|
||||
vram: Record<string, number>;
|
||||
tensor_split: number[];
|
||||
vram_utilization: number;
|
||||
}
|
||||
|
||||
export interface SubordinateWorkersItem {
|
||||
computed_resource_claim: ComputedResourceClaim1;
|
||||
ports: number[];
|
||||
worker_id: number;
|
||||
worker_name: string;
|
||||
gpu_type: string;
|
||||
gpu_indexes: number[];
|
||||
gpu_ids: string[];
|
||||
}
|
||||
|
||||
export interface InstanceSnapshot {
|
||||
computed_resource_claim: ComputedResourceClaim;
|
||||
ports: number[];
|
||||
worker_id: number;
|
||||
worker_name: string;
|
||||
gpu_type: string;
|
||||
gpu_indexes: number[];
|
||||
gpu_ids: string[];
|
||||
id: number;
|
||||
name: string;
|
||||
state: string;
|
||||
state_message: string;
|
||||
backend: string;
|
||||
backend_version: string;
|
||||
api_detected_backend_version: string;
|
||||
subordinate_workers: SubordinateWorkersItem[];
|
||||
}
|
||||
|
||||
export interface GPUSnapshot {
|
||||
vendor: string;
|
||||
type: string;
|
||||
index: number;
|
||||
device_index: number;
|
||||
device_chip_index: number;
|
||||
arch_family: string;
|
||||
name: string;
|
||||
uuid: string;
|
||||
driver_version: string;
|
||||
runtime_version: string;
|
||||
compute_capability: string;
|
||||
id: string;
|
||||
worker_id: number;
|
||||
worker_name: string;
|
||||
memory_total: number;
|
||||
core_total: number;
|
||||
}
|
||||
|
||||
export interface FormData {
|
||||
name: string;
|
||||
description: string;
|
||||
labels: Record<string, string>;
|
||||
cluster_id: number;
|
||||
model_id: number;
|
||||
model_name: string;
|
||||
model_instance_name: string;
|
||||
dataset_id: number;
|
||||
dataset_name: string;
|
||||
dataset_source: string;
|
||||
dataset_prompt_tokens: number;
|
||||
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;
|
||||
gpu_vendor_summary: string;
|
||||
}
|
||||
|
||||
export interface BenchmarkListItem extends FormData {
|
||||
id: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface DatasetListItem {
|
||||
name: string;
|
||||
source: string;
|
||||
prompt_tokens: number;
|
||||
output_tokens: number;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import SealInput from '@/components/seal-form/seal-input';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import React from 'react';
|
||||
import { FormData } from '../config/types';
|
||||
|
||||
const BasicForm: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const form = Form.useFormInstance();
|
||||
|
||||
return (
|
||||
<Form.Item<FormData>
|
||||
name="name"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage(
|
||||
{ id: 'common.form.rule.input' },
|
||||
{
|
||||
name: intl.formatMessage({ id: 'common.table.name' })
|
||||
}
|
||||
)
|
||||
}
|
||||
]}
|
||||
>
|
||||
<SealInput.Input
|
||||
label={intl.formatMessage({ id: 'common.table.name' })}
|
||||
required
|
||||
></SealInput.Input>
|
||||
</Form.Item>
|
||||
);
|
||||
};
|
||||
|
||||
export default BasicForm;
|
||||
@@ -0,0 +1,109 @@
|
||||
import IconFont from '@/components/icon-font';
|
||||
import { PageAction } from '@/config';
|
||||
import { PageActionType } from '@/config/types';
|
||||
import { useWrapperContext } from '@/pages/_components/column-wrapper/use-wrapper-context';
|
||||
import ScrollSpyTabs from '@/pages/_components/scroll-spy-tabs';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import {
|
||||
forwardRef,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useRef,
|
||||
useState
|
||||
} from 'react';
|
||||
import FormContext from '../config/form-context';
|
||||
import { FormData, BenchmarkListItem as ListItem } from '../config/types';
|
||||
import Basic from './basic';
|
||||
|
||||
interface ProviderFormProps {
|
||||
ref?: any;
|
||||
action: PageActionType;
|
||||
currentData?: ListItem; // Used when action is EDIT
|
||||
onFinish: (values: FormData) => Promise<void>;
|
||||
}
|
||||
|
||||
const TABKeysMap = {
|
||||
BASIC: 'basic',
|
||||
SUPPORTEDMODELS: 'supportedModels',
|
||||
CUSTOMCONFIG: 'customConfig',
|
||||
ADVANCED: 'advanced'
|
||||
};
|
||||
|
||||
const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
|
||||
const { action, currentData, onFinish } = props;
|
||||
const intl = useIntl();
|
||||
const [form] = Form.useForm();
|
||||
const { getScrollElementScrollableHeight } = useWrapperContext();
|
||||
const [activeKey, setActiveKey] = useState<string[]>([TABKeysMap.BASIC]);
|
||||
const scrollTabsRef = useRef<any>(null);
|
||||
|
||||
const segmentOptions = [
|
||||
{
|
||||
value: TABKeysMap.BASIC,
|
||||
label: 'Basic',
|
||||
icon: <IconFont type="icon-basic" />,
|
||||
field: 'name'
|
||||
},
|
||||
{
|
||||
value: TABKeysMap.SUPPORTEDMODELS,
|
||||
label: 'Supported Models',
|
||||
icon: <IconFont type="icon-speed" />,
|
||||
field: 'supportedModels'
|
||||
},
|
||||
{
|
||||
value: TABKeysMap.ADVANCED,
|
||||
label: intl.formatMessage({ id: 'resources.form.advanced' }),
|
||||
icon: <IconFont type="icon-settings" />,
|
||||
field: 'advanceConfig'
|
||||
}
|
||||
];
|
||||
|
||||
const handleActiveChange = (key: string[]) => {
|
||||
setActiveKey(key);
|
||||
};
|
||||
|
||||
const handleOnCollapseChange = (keys: string | string[]) => {
|
||||
setActiveKey(Array.isArray(keys) ? keys : [keys]);
|
||||
};
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
submit: () => {
|
||||
form.submit();
|
||||
},
|
||||
resetFields: () => {
|
||||
form.resetFields();
|
||||
}
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
if (action === PageAction.EDIT && currentData) {
|
||||
form.setFieldsValue({
|
||||
...currentData
|
||||
});
|
||||
}
|
||||
}, [form, currentData, action]);
|
||||
|
||||
return (
|
||||
<ScrollSpyTabs
|
||||
ref={scrollTabsRef}
|
||||
defaultTarget="basic"
|
||||
segmentOptions={[]}
|
||||
activeKey={activeKey}
|
||||
setActiveKey={handleActiveChange}
|
||||
segmentedTop={{
|
||||
top: 0,
|
||||
offsetTop: 96
|
||||
}}
|
||||
getScrollElementScrollableHeight={getScrollElementScrollableHeight}
|
||||
>
|
||||
<FormContext.Provider value={{ action }}>
|
||||
<Form form={form} onFinish={onFinish} initialValues={{}}>
|
||||
<Basic />
|
||||
</Form>
|
||||
</FormContext.Provider>
|
||||
</ScrollSpyTabs>
|
||||
);
|
||||
});
|
||||
|
||||
export default ProviderForm;
|
||||
@@ -0,0 +1,176 @@
|
||||
// columns.ts
|
||||
import AutoTooltip from '@/components/auto-tooltip';
|
||||
import DropdownButtons from '@/components/drop-down-buttons';
|
||||
import icons from '@/components/icon-font/icons';
|
||||
import StatusTag from '@/components/status-tag';
|
||||
import { tableSorter } from '@/config/settings';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { ColumnsType } from 'antd/es/table';
|
||||
import dayjs from 'dayjs';
|
||||
import { useMemo } from 'react';
|
||||
import { BenchmarkStatus, BenchmarkStatusLabelMap } from '../config';
|
||||
import { BenchmarkListItem as ListItem } from '../config/types';
|
||||
|
||||
const actionList = [
|
||||
{
|
||||
key: 'edit',
|
||||
label: 'common.button.edit',
|
||||
icon: icons.EditOutlined
|
||||
},
|
||||
{
|
||||
key: 'delete',
|
||||
label: 'common.button.delete',
|
||||
icon: icons.DeleteOutlined,
|
||||
props: {
|
||||
danger: true
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const useBenchmarkColumns = (
|
||||
sortOrder: string[],
|
||||
handleSelect: (val: string, record: ListItem) => void
|
||||
): ColumnsType<ListItem> => {
|
||||
const intl = useIntl();
|
||||
|
||||
return useMemo(() => {
|
||||
return [
|
||||
{
|
||||
title: intl.formatMessage({ id: 'common.table.name' }),
|
||||
dataIndex: 'name',
|
||||
sorter: tableSorter(1),
|
||||
render: (text: string) => (
|
||||
<AutoTooltip ghost minWidth={20}>
|
||||
{text}
|
||||
</AutoTooltip>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'benchmark.table.model' }),
|
||||
dataIndex: 'model_name',
|
||||
sorter: tableSorter(1),
|
||||
render: (text: string) => (
|
||||
<AutoTooltip ghost minWidth={20}>
|
||||
{text}
|
||||
</AutoTooltip>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'benchmark.table.dataset' }),
|
||||
dataIndex: 'dataset_name',
|
||||
sorter: tableSorter(1),
|
||||
render: (text: string) => (
|
||||
<AutoTooltip ghost minWidth={20}>
|
||||
{text}
|
||||
</AutoTooltip>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'common.table.status' }),
|
||||
dataIndex: 'state',
|
||||
render: (value: number, record: ListItem) => (
|
||||
<StatusTag
|
||||
statusValue={{
|
||||
status: BenchmarkStatus[value],
|
||||
text: BenchmarkStatusLabelMap[value],
|
||||
message: record.state_message || undefined
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'benchmark.table.requestRate' }),
|
||||
dataIndex: 'request_rate',
|
||||
sorter: tableSorter(1),
|
||||
render: (text: string) => (
|
||||
<AutoTooltip ghost minWidth={20}>
|
||||
{text}
|
||||
</AutoTooltip>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'benchmark.table.gpu' }),
|
||||
dataIndex: 'gpu_summary',
|
||||
sorter: tableSorter(1),
|
||||
render: (text: string) => (
|
||||
<AutoTooltip ghost minWidth={20}>
|
||||
{text}
|
||||
</AutoTooltip>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'benchmark.table.itl' }),
|
||||
dataIndex: 'itl',
|
||||
sorter: tableSorter(1),
|
||||
render: (text: string) => (
|
||||
<AutoTooltip ghost minWidth={20}>
|
||||
{text}
|
||||
</AutoTooltip>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'benchmark.table.tpot' }),
|
||||
dataIndex: 'tpot',
|
||||
sorter: tableSorter(1),
|
||||
render: (text: string) => (
|
||||
<AutoTooltip ghost minWidth={20}>
|
||||
{text}
|
||||
</AutoTooltip>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'benchmark.table.ttft' }),
|
||||
dataIndex: 'ttft',
|
||||
sorter: tableSorter(1),
|
||||
render: (text: string) => (
|
||||
<AutoTooltip ghost minWidth={20}>
|
||||
{text}
|
||||
</AutoTooltip>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'benchmark.table.rps' }),
|
||||
dataIndex: 'requests_per_second',
|
||||
sorter: tableSorter(1),
|
||||
render: (text: string) => (
|
||||
<AutoTooltip ghost minWidth={20}>
|
||||
{text}
|
||||
</AutoTooltip>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'benchmark.table.tps' }),
|
||||
dataIndex: 'tokens_per_second',
|
||||
sorter: tableSorter(1),
|
||||
render: (text: string) => (
|
||||
<AutoTooltip ghost minWidth={20}>
|
||||
{text}
|
||||
</AutoTooltip>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'common.table.createTime' }),
|
||||
dataIndex: 'created_at',
|
||||
sorter: tableSorter(3),
|
||||
render: (value: string) => (
|
||||
<span>{dayjs(value).format('YYYY-MM-DD HH:mm:ss')}</span>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'common.table.operation' }),
|
||||
dataIndex: 'operations',
|
||||
ellipsis: {
|
||||
showTitle: false
|
||||
},
|
||||
render: (value: string, record: ListItem) => (
|
||||
<DropdownButtons
|
||||
items={actionList}
|
||||
onSelect={(val) => handleSelect(val, record)}
|
||||
></DropdownButtons>
|
||||
)
|
||||
}
|
||||
];
|
||||
}, [intl, handleSelect]);
|
||||
};
|
||||
|
||||
export default useBenchmarkColumns;
|
||||
@@ -0,0 +1,46 @@
|
||||
import { PageAction } from '@/config';
|
||||
import { PageActionType } from '@/config/types';
|
||||
import { useState } from 'react';
|
||||
import { BenchmarkListItem as ListItem } from '../config/types';
|
||||
|
||||
const useCreateBenchmark = () => {
|
||||
const [openModalStatus, setOpenModalStatus] = useState<{
|
||||
open: boolean;
|
||||
action: PageActionType;
|
||||
currentData?: ListItem;
|
||||
title: string;
|
||||
}>({
|
||||
open: false,
|
||||
action: PageAction.CREATE,
|
||||
currentData: undefined,
|
||||
title: ''
|
||||
});
|
||||
|
||||
const openModal = (action: PageActionType, title: string, row?: ListItem) => {
|
||||
setOpenModalStatus({
|
||||
...openModalStatus,
|
||||
open: true,
|
||||
title: title,
|
||||
action,
|
||||
currentData: row
|
||||
});
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
setOpenModalStatus({
|
||||
open: false,
|
||||
action: PageAction.CREATE,
|
||||
currentData: undefined,
|
||||
title: ''
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
openBenchmarkModalStatus: openModalStatus,
|
||||
setOpenBenchmarkModalStatus: setOpenModalStatus,
|
||||
openBenchmarkModal: openModal,
|
||||
closeBenchmarkModal: closeModal
|
||||
};
|
||||
};
|
||||
|
||||
export default useCreateBenchmark;
|
||||
@@ -0,0 +1,40 @@
|
||||
import { PageActionType } from '@/config/types';
|
||||
import { useState } from 'react';
|
||||
import { BenchmarkListItem as ListItem } from '../config/types';
|
||||
|
||||
const useCreateCompare = () => {
|
||||
const [openModalStatus, setOpenModalStatus] = useState<{
|
||||
open: boolean;
|
||||
currentData?: ListItem[];
|
||||
}>({
|
||||
open: false,
|
||||
currentData: undefined
|
||||
});
|
||||
|
||||
const openModal = (
|
||||
action: PageActionType,
|
||||
title: string,
|
||||
rows?: ListItem[]
|
||||
) => {
|
||||
setOpenModalStatus({
|
||||
open: true,
|
||||
currentData: rows
|
||||
});
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
setOpenModalStatus({
|
||||
open: false,
|
||||
currentData: undefined
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
openCompareModalStatus: openModalStatus,
|
||||
setOpenCompareModalStatus: setOpenModalStatus,
|
||||
openCompareModal: openModal,
|
||||
closeCompareModal: closeModal
|
||||
};
|
||||
};
|
||||
|
||||
export default useCreateCompare;
|
||||
@@ -0,0 +1,172 @@
|
||||
import DeleteModal from '@/components/delete-modal';
|
||||
import IconFont from '@/components/icon-font';
|
||||
import { FilterBar } from '@/components/page-tools';
|
||||
import { PageAction } from '@/config';
|
||||
import { TABLE_SORT_DIRECTIONS } from '@/config/settings';
|
||||
import useTableFetch from '@/hooks/use-table-fetch';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { ConfigProvider, Table, message } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import NoResult from '../_components/no-result';
|
||||
import PageBox from '../_components/page-box';
|
||||
import {
|
||||
createBenchmark,
|
||||
deleteBenchmark,
|
||||
queryBenchmarkList,
|
||||
updateBenchmark
|
||||
} from './apis';
|
||||
import AddBenchmarkModal from './components/add-benchmark-modal';
|
||||
import RightActions from './components/right-actions';
|
||||
import { FormData, BenchmarkListItem as ListItem } from './config/types';
|
||||
import useBenchmarkColumns from './hooks/use-benchmark-columns';
|
||||
import useCreateBenchmark from './hooks/use-create-benchmark';
|
||||
|
||||
const Benchmark: React.FC = () => {
|
||||
const {
|
||||
dataSource,
|
||||
rowSelection,
|
||||
queryParams,
|
||||
sortOrder,
|
||||
modalRef,
|
||||
handleDelete,
|
||||
handleDeleteBatch,
|
||||
fetchData,
|
||||
handlePageChange,
|
||||
handleTableChange,
|
||||
handleSearch,
|
||||
handleNameChange
|
||||
} = useTableFetch<ListItem>({
|
||||
fetchAPI: queryBenchmarkList,
|
||||
deleteAPI: deleteBenchmark,
|
||||
contentForDelete: 'menu.models.benchmark'
|
||||
});
|
||||
const intl = useIntl();
|
||||
const { openBenchmarkModal, closeBenchmarkModal, openBenchmarkModalStatus } =
|
||||
useCreateBenchmark();
|
||||
|
||||
const handleAddBenchmark = () => {
|
||||
openBenchmarkModal(PageAction.CREATE, 'Add Benchmark');
|
||||
};
|
||||
|
||||
const handleModalOk = async (data: FormData) => {
|
||||
const params = {
|
||||
...data
|
||||
};
|
||||
try {
|
||||
if (openBenchmarkModalStatus.action === PageAction.EDIT) {
|
||||
await updateBenchmark({
|
||||
data: {
|
||||
...params
|
||||
},
|
||||
id: openBenchmarkModalStatus.currentData!.id
|
||||
});
|
||||
} else {
|
||||
await createBenchmark({ data: params });
|
||||
}
|
||||
fetchData();
|
||||
closeBenchmarkModal();
|
||||
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
||||
} catch (error) {
|
||||
closeBenchmarkModal();
|
||||
}
|
||||
};
|
||||
|
||||
const handleModalCancel = () => {
|
||||
closeBenchmarkModal();
|
||||
};
|
||||
|
||||
const handleEditUser = (row: ListItem) => {
|
||||
openBenchmarkModal(PageAction.EDIT, 'Edit Benchmark', row);
|
||||
};
|
||||
|
||||
const handleSelect = useMemoizedFn((val: any, row: ListItem) => {
|
||||
if (val === 'edit') {
|
||||
handleEditUser(row);
|
||||
} else if (val === 'delete') {
|
||||
handleDelete({ ...row, name: row.name });
|
||||
}
|
||||
});
|
||||
|
||||
const renderEmpty = (type?: string) => {
|
||||
if (type !== 'Table') return;
|
||||
return (
|
||||
<NoResult
|
||||
loading={dataSource.loading}
|
||||
loadend={dataSource.loadend}
|
||||
dataSource={[]}
|
||||
image={<IconFont type="icon-credential-outline" />}
|
||||
filters={_.omit(queryParams, ['sort_by'])}
|
||||
noFoundText={intl.formatMessage({
|
||||
id: 'noresult.benchmark.nofound'
|
||||
})}
|
||||
title={intl.formatMessage({ id: 'noresult.benchmark.title' })}
|
||||
subTitle={intl.formatMessage({
|
||||
id: 'noresult.benchmark.subTitle'
|
||||
})}
|
||||
onClick={handleAddBenchmark}
|
||||
buttonText={intl.formatMessage({ id: 'noresult.button.add' })}
|
||||
></NoResult>
|
||||
);
|
||||
};
|
||||
|
||||
const columns = useBenchmarkColumns(sortOrder, handleSelect);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageBox>
|
||||
<FilterBar
|
||||
showSelect={false}
|
||||
marginBottom={22}
|
||||
marginTop={0}
|
||||
handleSearch={handleSearch}
|
||||
handleInputChange={handleNameChange}
|
||||
rowSelection={rowSelection}
|
||||
widths={{ input: 300 }}
|
||||
right={
|
||||
<RightActions
|
||||
handleDeleteByBatch={handleDeleteBatch}
|
||||
handleClickPrimary={handleAddBenchmark}
|
||||
buttonText={intl.formatMessage({
|
||||
id: 'benchmark.button.add'
|
||||
})}
|
||||
rowSelection={rowSelection}
|
||||
/>
|
||||
}
|
||||
></FilterBar>
|
||||
<ConfigProvider renderEmpty={renderEmpty}>
|
||||
<Table
|
||||
tableLayout="fixed"
|
||||
columns={columns}
|
||||
dataSource={dataSource.dataList}
|
||||
rowSelection={rowSelection}
|
||||
loading={dataSource.loading}
|
||||
sortDirections={TABLE_SORT_DIRECTIONS}
|
||||
showSorterTooltip={false}
|
||||
rowKey="id"
|
||||
onChange={handleTableChange}
|
||||
pagination={{
|
||||
showSizeChanger: true,
|
||||
pageSize: queryParams.perPage,
|
||||
current: queryParams.page,
|
||||
total: dataSource.total,
|
||||
hideOnSinglePage: queryParams.perPage === 10,
|
||||
onChange: handlePageChange
|
||||
}}
|
||||
></Table>
|
||||
</ConfigProvider>
|
||||
</PageBox>
|
||||
<AddBenchmarkModal
|
||||
open={openBenchmarkModalStatus.open}
|
||||
action={openBenchmarkModalStatus.action}
|
||||
title={openBenchmarkModalStatus.title}
|
||||
currentData={openBenchmarkModalStatus.currentData}
|
||||
onCancel={handleModalCancel}
|
||||
onOk={handleModalOk}
|
||||
></AddBenchmarkModal>
|
||||
<DeleteModal ref={modalRef}></DeleteModal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Benchmark;
|
||||
@@ -693,7 +693,7 @@ const Models: React.FC<ModelsProps> = ({
|
||||
</Space>
|
||||
}
|
||||
right={
|
||||
<Space size={20}>
|
||||
<Space size={16}>
|
||||
{page !== 'clusters' && (
|
||||
<DropDownActions
|
||||
menu={{
|
||||
|
||||
Reference in New Issue
Block a user