chore: profile config
This commit is contained in:
Vendored
+1
-1
@@ -56,7 +56,7 @@ declare namespace Global {
|
|||||||
currentUser?: UserInfo;
|
currentUser?: UserInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
type SearchParams = Pagination & { search?: string };
|
type SearchParams = Pagination & { search?: string; [key: string]: any };
|
||||||
|
|
||||||
type MessageType = 'transition' | 'warning' | 'danger' | 'success' | 'info';
|
type MessageType = 'transition' | 'warning' | 'danger' | 'success' | 'info';
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
import { request } from '@umijs/max';
|
import { request } from '@umijs/max';
|
||||||
import { CancelToken } from 'axios';
|
import { CancelToken } from 'axios';
|
||||||
import { BenchmarkMetricsFormData } from '../config/detail-types';
|
import {
|
||||||
import { BenchmarkListItem, DatasetListItem, FormData } from '../config/types';
|
BenchmarkListItem,
|
||||||
|
DatasetListItem,
|
||||||
|
FormData,
|
||||||
|
ProfileOption
|
||||||
|
} from '../config/types';
|
||||||
|
|
||||||
export const BENCHMARKS_API = '/benchmarks';
|
export const BENCHMARKS_API = '/benchmarks';
|
||||||
export const DATASETS_API = '/datasets';
|
export const DATASETS_API = '/datasets';
|
||||||
|
export const PROFILES_CONFIG_API = '/benchmark-profiles/default-config';
|
||||||
|
|
||||||
export async function queryBenchmarkList(
|
export async function queryBenchmarkList(
|
||||||
params: Global.SearchParams,
|
params: Global.SearchParams,
|
||||||
@@ -83,18 +88,18 @@ export async function queryDatasetList(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function queryBenchmarkMetrics(
|
export async function queryProfiles(
|
||||||
params: {
|
params: {
|
||||||
id: number;
|
id?: number | string;
|
||||||
data: BenchmarkMetricsFormData;
|
|
||||||
},
|
},
|
||||||
options?: {
|
options?: {
|
||||||
token?: CancelToken;
|
token?: CancelToken;
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
return request<any>(`${BENCHMARKS_API}/${params.id}/metrics`, {
|
return request<{
|
||||||
method: 'POST',
|
profiles: ProfileOption[];
|
||||||
data: params.data,
|
}>(`${PROFILES_CONFIG_API}`, {
|
||||||
|
method: 'get',
|
||||||
cancelToken: options?.token
|
cancelToken: options?.token
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { Descriptions, DescriptionsProps } from 'antd';
|
||||||
|
import React from 'react';
|
||||||
|
import { useDetailContext } from '../../config/detail-context';
|
||||||
|
import Section from '../summary/section';
|
||||||
|
|
||||||
|
const Benchmark: React.FC = () => {
|
||||||
|
const { detailData } = useDetailContext();
|
||||||
|
|
||||||
|
const items: DescriptionsProps['items'] = [
|
||||||
|
{
|
||||||
|
key: '1',
|
||||||
|
label: 'Profile',
|
||||||
|
children: detailData?.profile || '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '2',
|
||||||
|
label: 'Dataset',
|
||||||
|
children: detailData?.dataset_name || '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '3',
|
||||||
|
label: 'Token Length (In/Out)',
|
||||||
|
children: (
|
||||||
|
<span>
|
||||||
|
{detailData?.dataset_input_tokens || '-'} /{' '}
|
||||||
|
{detailData?.dataset_output_tokens || '-'}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '5',
|
||||||
|
label: 'Seed',
|
||||||
|
children: detailData?.seed || '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '6',
|
||||||
|
label: 'Request Rate',
|
||||||
|
children: detailData?.request_rate || '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '7',
|
||||||
|
label: 'Total Requests',
|
||||||
|
children: detailData?.total_requests || '-'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Section title="Benchmark Parameters">
|
||||||
|
<Descriptions
|
||||||
|
items={items}
|
||||||
|
colon={false}
|
||||||
|
column={3}
|
||||||
|
layout="vertical"
|
||||||
|
styles={{
|
||||||
|
content: {
|
||||||
|
justifyContent: 'flex-start'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
></Descriptions>
|
||||||
|
</Section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Benchmark;
|
||||||
@@ -1,9 +1,23 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
import { useDetailContext } from '../../config/detail-context';
|
import { useDetailContext } from '../../config/detail-context';
|
||||||
|
import Benchmark from './benchmark';
|
||||||
|
import Instance from './instance';
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
`;
|
||||||
|
|
||||||
const Configure: React.FC = () => {
|
const Configure: React.FC = () => {
|
||||||
const { detailData } = useDetailContext();
|
const { detailData } = useDetailContext();
|
||||||
return <div>Configure Content</div>;
|
return (
|
||||||
|
<Container>
|
||||||
|
<Instance />
|
||||||
|
<Benchmark />
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Configure;
|
export default Configure;
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
import StatusTag from '@/components/status-tag';
|
||||||
|
import { InstanceStatusMapValue, status } from '@/pages/llmodels/config';
|
||||||
|
import { convertFileSize } from '@/utils';
|
||||||
|
import { Descriptions } from 'antd';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import { useDetailContext } from '../../config/detail-context';
|
||||||
|
import Section from '../summary/section';
|
||||||
|
|
||||||
|
const calcTotalVram = (vram: Record<string, number>) => {
|
||||||
|
return _.sum(_.values(vram));
|
||||||
|
};
|
||||||
|
|
||||||
|
const Instance: React.FC = () => {
|
||||||
|
const { detailData } = useDetailContext();
|
||||||
|
|
||||||
|
const items = useMemo(() => {
|
||||||
|
const { snapshot } = detailData;
|
||||||
|
const [instanceName, instanceData] =
|
||||||
|
Object.entries(snapshot.instances || {})[0] || [];
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
key: '1',
|
||||||
|
label: 'Instance Name',
|
||||||
|
children: (
|
||||||
|
<div className="flex-center gap-8">
|
||||||
|
<span>{instanceName}</span>
|
||||||
|
<StatusTag
|
||||||
|
statusValue={{
|
||||||
|
status: status[instanceData.state],
|
||||||
|
text: InstanceStatusMapValue[instanceData.state],
|
||||||
|
message: detailData.state_message || undefined
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '2',
|
||||||
|
label: 'Worker',
|
||||||
|
children: instanceData?.worker_name || '-'
|
||||||
|
// children:
|
||||||
|
// instanceData?.ports?.length > 0
|
||||||
|
// ? `${instanceData.worker_ip}:${instanceData.ports[0]}`
|
||||||
|
// : instanceData.worker_ip || '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '3',
|
||||||
|
label: 'GPU Indexes',
|
||||||
|
children:
|
||||||
|
_.join(
|
||||||
|
instanceData.gpu_indexes?.sort?.((a, b) => a - b),
|
||||||
|
','
|
||||||
|
) || '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '6',
|
||||||
|
label: 'GPU Type',
|
||||||
|
children: instanceData?.gpu_type || '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '4',
|
||||||
|
label: 'Allocated VRAM',
|
||||||
|
children:
|
||||||
|
convertFileSize(
|
||||||
|
instanceData.computed_resource_claim?.vram
|
||||||
|
? calcTotalVram(instanceData.computed_resource_claim?.vram)
|
||||||
|
: 0,
|
||||||
|
1
|
||||||
|
) || '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '5',
|
||||||
|
label: 'Backend',
|
||||||
|
children: `${instanceData?.backend || '-'} ${
|
||||||
|
instanceData.backend_version
|
||||||
|
? `(${instanceData.backend_version})`
|
||||||
|
: ''
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}, [detailData]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Section title="Model Instance">
|
||||||
|
<Descriptions
|
||||||
|
items={items}
|
||||||
|
colon={false}
|
||||||
|
column={3}
|
||||||
|
layout="vertical"
|
||||||
|
styles={{
|
||||||
|
content: {
|
||||||
|
justifyContent: 'flex-start'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
></Descriptions>
|
||||||
|
</Section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Instance;
|
||||||
@@ -1,16 +1,28 @@
|
|||||||
|
import AutoTooltip from '@/components/auto-tooltip';
|
||||||
import { convertFileSize } from '@/utils';
|
import { convertFileSize } from '@/utils';
|
||||||
import { Descriptions, DescriptionsProps } from 'antd';
|
import { Descriptions, DescriptionsProps } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
import { GPUData } from '../../config/detail-types';
|
import { GPUData } from '../../config/detail-types';
|
||||||
import Section from '../summary/section';
|
import Section from '../summary/section';
|
||||||
|
|
||||||
|
const Content = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: flex-start;
|
||||||
|
.name {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
const Environment: React.FC<GPUData> = (props) => {
|
const Environment: React.FC<GPUData> = (props) => {
|
||||||
const items: DescriptionsProps['items'] = [
|
const items: DescriptionsProps['items'] = [
|
||||||
{
|
// {
|
||||||
key: '4',
|
// key: '4',
|
||||||
label: 'Name',
|
// label: 'Name',
|
||||||
children: props.name
|
// children: <AutoTooltip ghost>{props.name}</AutoTooltip>
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
key: '1',
|
key: '1',
|
||||||
label: 'VRAM',
|
label: 'VRAM',
|
||||||
@@ -18,12 +30,12 @@ const Environment: React.FC<GPUData> = (props) => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: '3',
|
key: '3',
|
||||||
label: 'Driver Version',
|
label: 'Driver',
|
||||||
children: props.driver_version
|
children: props.driver_version
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: '2',
|
key: '2',
|
||||||
label: 'Runtime Version',
|
label: 'Runtime',
|
||||||
children: props.runtime_version
|
children: props.runtime_version
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -35,21 +47,36 @@ const Environment: React.FC<GPUData> = (props) => {
|
|||||||
key: '5',
|
key: '5',
|
||||||
label: 'Vendor',
|
label: 'Vendor',
|
||||||
children: props.vendor
|
children: props.vendor
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '7',
|
||||||
|
label: 'GPU Type',
|
||||||
|
children: props.type
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
return (
|
return (
|
||||||
<Section title={`GPU ${props.index}`}>
|
<Section
|
||||||
<Descriptions
|
title={
|
||||||
items={items}
|
<span style={{ color: 'var(--ant-color-text-tertiary)' }}>
|
||||||
colon={false}
|
GPU {props.index}
|
||||||
column={3}
|
</span>
|
||||||
layout="vertical"
|
}
|
||||||
styles={{
|
>
|
||||||
content: {
|
<Content>
|
||||||
justifyContent: 'flex-start'
|
<div className="name">
|
||||||
}
|
<AutoTooltip ghost>{props.name}</AutoTooltip>
|
||||||
}}
|
</div>
|
||||||
></Descriptions>
|
<Descriptions
|
||||||
|
items={items}
|
||||||
|
colon={true}
|
||||||
|
column={3}
|
||||||
|
styles={{
|
||||||
|
content: {
|
||||||
|
justifyContent: 'flex-start'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
></Descriptions>
|
||||||
|
</Content>
|
||||||
</Section>
|
</Section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -63,7 +63,14 @@ const Environment: React.FC = () => {
|
|||||||
gpuData={mainWorker.gpuData}
|
gpuData={mainWorker.gpuData}
|
||||||
title={
|
title={
|
||||||
<div className="flex-center gap-8">
|
<div className="flex-center gap-8">
|
||||||
<Tag color="geekblue">Sub</Tag>
|
<Tag
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'var(--ant-color-fill-secondary)',
|
||||||
|
color: 'var(--ant-color-text-tertiary)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Sub
|
||||||
|
</Tag>
|
||||||
<span>{mainWorker?.workerData?.name}</span>
|
<span>{mainWorker?.workerData?.name}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ const Environment: React.FC<{
|
|||||||
<Section
|
<Section
|
||||||
title={title}
|
title={title}
|
||||||
styles={{
|
styles={{
|
||||||
|
wraper: {
|
||||||
|
border: 'none',
|
||||||
|
overflow: 'unset'
|
||||||
|
},
|
||||||
container: {
|
container: {
|
||||||
border: 'none',
|
border: 'none',
|
||||||
paddingInline: 16,
|
paddingInline: 16,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import BaseSelect from '@/components/seal-form/base/select';
|
||||||
import { SearchOutlined, SyncOutlined } from '@ant-design/icons';
|
import { SearchOutlined, SyncOutlined } from '@ant-design/icons';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Button, Input, Space } from 'antd';
|
import { Button, Input, Space } from 'antd';
|
||||||
@@ -6,11 +7,19 @@ import React from 'react';
|
|||||||
export interface RightActionsProps {
|
export interface RightActionsProps {
|
||||||
handleInputChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
handleInputChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||||
handleSearch: () => void;
|
handleSearch: () => void;
|
||||||
|
handleQueryChange: (value: any, option?: any) => void;
|
||||||
|
modelList?: Global.BaseOption<number>[];
|
||||||
|
datasetList?: Global.BaseOption<string>[];
|
||||||
|
gpuVendorList?: Global.BaseOption<string>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const RightActions: React.FC<RightActionsProps> = ({
|
const RightActions: React.FC<RightActionsProps> = ({
|
||||||
handleInputChange,
|
handleInputChange,
|
||||||
handleSearch
|
handleSearch,
|
||||||
|
handleQueryChange,
|
||||||
|
modelList,
|
||||||
|
datasetList,
|
||||||
|
gpuVendorList
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
@@ -29,7 +38,42 @@ const RightActions: React.FC<RightActionsProps> = ({
|
|||||||
allowClear
|
allowClear
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
></Input>
|
></Input>
|
||||||
{/* <CompareConditions></CompareConditions> */}
|
<BaseSelect
|
||||||
|
allowClear
|
||||||
|
placeholder="Filter by model"
|
||||||
|
style={{ width: 150 }}
|
||||||
|
options={modelList}
|
||||||
|
onChange={(value, option) =>
|
||||||
|
handleQueryChange({
|
||||||
|
model_name: value,
|
||||||
|
page: 1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
></BaseSelect>
|
||||||
|
<BaseSelect
|
||||||
|
allowClear
|
||||||
|
placeholder="Filter by dataset"
|
||||||
|
style={{ width: 150 }}
|
||||||
|
options={datasetList}
|
||||||
|
onChange={(value, option) =>
|
||||||
|
handleQueryChange({
|
||||||
|
dataset_name: value,
|
||||||
|
page: 1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
></BaseSelect>
|
||||||
|
<BaseSelect
|
||||||
|
allowClear
|
||||||
|
placeholder="Filter by GPU vendor"
|
||||||
|
onChange={(value, option) =>
|
||||||
|
handleQueryChange({
|
||||||
|
gpu_summary: value,
|
||||||
|
page: 1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
style={{ width: 180 }}
|
||||||
|
options={gpuVendorList}
|
||||||
|
></BaseSelect>
|
||||||
<Button
|
<Button
|
||||||
type="text"
|
type="text"
|
||||||
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export interface RightActionsProps {
|
|||||||
handleDeleteByBatch: () => void;
|
handleDeleteByBatch: () => void;
|
||||||
handleClickPrimary?: () => void;
|
handleClickPrimary?: () => void;
|
||||||
handleSettingFields?: () => void;
|
handleSettingFields?: () => void;
|
||||||
|
handleExport?: () => void;
|
||||||
settingButton?: React.ReactNode;
|
settingButton?: React.ReactNode;
|
||||||
buttonText?: string;
|
buttonText?: string;
|
||||||
rowSelection: {
|
rowSelection: {
|
||||||
@@ -23,6 +24,7 @@ const RightActions: React.FC<RightActionsProps> = ({
|
|||||||
handleDeleteByBatch,
|
handleDeleteByBatch,
|
||||||
handleClickPrimary,
|
handleClickPrimary,
|
||||||
handleSettingFields,
|
handleSettingFields,
|
||||||
|
handleExport,
|
||||||
settingButton,
|
settingButton,
|
||||||
buttonText,
|
buttonText,
|
||||||
rowSelection
|
rowSelection
|
||||||
@@ -36,7 +38,7 @@ const RightActions: React.FC<RightActionsProps> = ({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'common.button.delete',
|
label: 'common.button.delete',
|
||||||
key: 'start',
|
key: 'delete',
|
||||||
props: {
|
props: {
|
||||||
danger: true
|
danger: true
|
||||||
},
|
},
|
||||||
@@ -44,7 +46,13 @@ const RightActions: React.FC<RightActionsProps> = ({
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const handleActionSelect = (val: string) => {};
|
const handleActionSelect = (val: string) => {
|
||||||
|
if (val === 'delete') {
|
||||||
|
handleDeleteByBatch();
|
||||||
|
} else if (val === 'export') {
|
||||||
|
handleExport?.();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Space size={16}>
|
<Space size={16}>
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import AutoTooltip from '@/components/auto-tooltip';
|
import AutoTooltip from '@/components/auto-tooltip';
|
||||||
import StatusTag from '@/components/status-tag';
|
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Col, Descriptions, DescriptionsProps, Row, Statistic } from 'antd';
|
import { Col, Descriptions, DescriptionsProps, Row, Statistic } from 'antd';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { round } from 'lodash';
|
import { round } from 'lodash';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { BenchmarkStatus, BenchmarkStatusLabelMap } from '../../config';
|
|
||||||
import { useDetailContext } from '../../config/detail-context';
|
import { useDetailContext } from '../../config/detail-context';
|
||||||
import PercentileResult from './percentile-result';
|
import PercentileResult from './percentile-result';
|
||||||
import Section from './section';
|
import Section from './section';
|
||||||
@@ -158,7 +156,7 @@ const Summary: React.FC = () => {
|
|||||||
<Container>
|
<Container>
|
||||||
<Row gutter={16}>
|
<Row gutter={16}>
|
||||||
{cardFields.map((field) => (
|
{cardFields.map((field) => (
|
||||||
<Col span={8} key={field.key} style={{ padding: '8px' }}>
|
<Col span={4} key={field.key} style={{ padding: '8px' }}>
|
||||||
<Card>
|
<Card>
|
||||||
<Statistic
|
<Statistic
|
||||||
title={field.label}
|
title={field.label}
|
||||||
@@ -180,17 +178,7 @@ const Summary: React.FC = () => {
|
|||||||
</Col>
|
</Col>
|
||||||
))}
|
))}
|
||||||
</Row>
|
</Row>
|
||||||
<Section>
|
<Section title="Metadata">
|
||||||
<div className="flex-center section-title gap-8">
|
|
||||||
<span>{detailData.model_instance_name}</span>
|
|
||||||
<StatusTag
|
|
||||||
statusValue={{
|
|
||||||
status: BenchmarkStatus[detailData.state],
|
|
||||||
text: BenchmarkStatusLabelMap[detailData.state],
|
|
||||||
message: detailData.state_message || undefined
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Descriptions
|
<Descriptions
|
||||||
items={items}
|
items={items}
|
||||||
colon={false}
|
colon={false}
|
||||||
@@ -205,10 +193,7 @@ const Summary: React.FC = () => {
|
|||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Section>
|
<Section title="Throughput">
|
||||||
<div className="flex-center section-title gap-8">
|
|
||||||
<span>Throughput</span>
|
|
||||||
</div>
|
|
||||||
<Descriptions
|
<Descriptions
|
||||||
items={throughputFields}
|
items={throughputFields}
|
||||||
colon={true}
|
colon={true}
|
||||||
@@ -220,10 +205,7 @@ const Summary: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
></Descriptions>
|
></Descriptions>
|
||||||
</Section>
|
</Section>
|
||||||
<Section>
|
<Section title="Latency">
|
||||||
<div className="flex-center section-title gap-8">
|
|
||||||
<span>Latency</span>
|
|
||||||
</div>
|
|
||||||
<Descriptions
|
<Descriptions
|
||||||
items={latencyFields}
|
items={latencyFields}
|
||||||
colon={true}
|
colon={true}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import _ from 'lodash';
|
import React from 'react';
|
||||||
import React, { useEffect } from 'react';
|
|
||||||
import { useDetailContext } from '../../config/detail-context';
|
import { useDetailContext } from '../../config/detail-context';
|
||||||
import useQueryMtrics from '../../services/use-query-metrics';
|
|
||||||
|
|
||||||
const fields = [
|
const fields = [
|
||||||
'requests_per_second_mean',
|
'requests_per_second_mean',
|
||||||
@@ -15,25 +13,7 @@ const fields = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const Summary: React.FC = () => {
|
const Summary: React.FC = () => {
|
||||||
const { detailData, id } = useDetailContext();
|
const { detailData } = useDetailContext();
|
||||||
const {
|
|
||||||
detailData: metricsData,
|
|
||||||
fetchData,
|
|
||||||
cancelRequest
|
|
||||||
} = useQueryMtrics();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (id) {
|
|
||||||
fetchData({
|
|
||||||
id: id,
|
|
||||||
data: {
|
|
||||||
..._.pick(detailData, fields)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
cancelRequest();
|
|
||||||
}
|
|
||||||
}, [id]);
|
|
||||||
return <div></div>;
|
return <div></div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
border: 1px solid var(--ant-color-border);
|
||||||
|
border-radius: var(--ant-border-radius);
|
||||||
|
overflow: hidden;
|
||||||
|
`;
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
border: 1px solid var(--ant-color-border);
|
// border: 1px solid var(--ant-color-border);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
.section-title {
|
.section-title {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@@ -17,25 +23,29 @@ const Container = styled.div`
|
|||||||
const Title = styled.div`
|
const Title = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
margin-bottom: 12px;
|
// margin-bottom: 12px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
padding: 12px 16px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
background-color: var(--ant-color-fill-tertiary);
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const DetailSection: React.FC<{
|
const DetailSection: React.FC<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
title?: React.ReactNode;
|
title?: React.ReactNode;
|
||||||
styles?: {
|
styles?: {
|
||||||
|
wraper?: React.CSSProperties;
|
||||||
container?: React.CSSProperties;
|
container?: React.CSSProperties;
|
||||||
title?: React.CSSProperties;
|
title?: React.CSSProperties;
|
||||||
};
|
};
|
||||||
}> = ({ children, title, styles }) => {
|
}> = ({ children, title, styles }) => {
|
||||||
return (
|
return (
|
||||||
<Container style={styles?.container}>
|
<Wrapper style={styles?.wraper}>
|
||||||
{title && <Title style={styles?.title}>{title}</Title>}
|
{title && <Title style={styles?.title}>{title}</Title>}
|
||||||
{children}
|
<Container style={styles?.container}>{children}</Container>
|
||||||
</Container>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -28,26 +28,46 @@ export const BenchmarkStatus: Record<string, StatusType> = {
|
|||||||
[BenchmarkStatusValueMap.Unreachable]: StatusMaps.error
|
[BenchmarkStatusValueMap.Unreachable]: StatusMaps.error
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ProfileValueMap = {
|
||||||
|
LatencyShort: 'latency_short',
|
||||||
|
ThroughputMedium: 'throughput_medium',
|
||||||
|
LongContextStress: 'long_context_stress',
|
||||||
|
GenerationHeavy: 'generation_heavy',
|
||||||
|
Custom: 'Custom'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ProfileLabelMap = {
|
||||||
|
[ProfileValueMap.LatencyShort]: 'benchmark.form.profile.latency',
|
||||||
|
[ProfileValueMap.ThroughputMedium]: 'benchmark.form.profile.throughput',
|
||||||
|
[ProfileValueMap.LongContextStress]: 'benchmark.form.profile.longContext',
|
||||||
|
[ProfileValueMap.GenerationHeavy]: 'benchmark.form.profile.heavy',
|
||||||
|
[ProfileValueMap.Custom]: 'benchmark.form.profile.custom'
|
||||||
|
};
|
||||||
|
|
||||||
export const profileOptions = [
|
export const profileOptions = [
|
||||||
{
|
{
|
||||||
label: 'benchmark.form.profile.latency',
|
label: 'benchmark.form.profile.latency',
|
||||||
value: 'latency_short',
|
value: ProfileValueMap.LatencyShort,
|
||||||
locale: true
|
locale: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'benchmark.form.profile.throughput',
|
label: 'benchmark.form.profile.throughput',
|
||||||
value: 'throughput_medium',
|
value: ProfileValueMap.ThroughputMedium,
|
||||||
locale: true
|
locale: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'benchmark.form.profile.longContext',
|
label: 'benchmark.form.profile.longContext',
|
||||||
value: 'long_context_stress',
|
value: ProfileValueMap.LongContextStress,
|
||||||
locale: true
|
locale: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'benchmark.form.profile.heavy',
|
label: 'benchmark.form.profile.heavy',
|
||||||
value: 'generation_heavy',
|
value: ProfileValueMap.GenerationHeavy,
|
||||||
locale: true
|
locale: true
|
||||||
},
|
},
|
||||||
{ label: 'benchmark.form.profile.custom', value: 'custom', locale: true }
|
{
|
||||||
|
label: 'benchmark.form.profile.custom',
|
||||||
|
value: ProfileValueMap.Custom,
|
||||||
|
locale: true
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ export interface FormData {
|
|||||||
state_message: string;
|
state_message: string;
|
||||||
worker_id: number;
|
worker_id: number;
|
||||||
gpu_summary: string;
|
gpu_summary: string;
|
||||||
|
seed: number;
|
||||||
gpu_vendor_summary: string;
|
gpu_vendor_summary: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,4 +102,16 @@ export interface DatasetListItem {
|
|||||||
source: string;
|
source: string;
|
||||||
prompt_tokens: number;
|
prompt_tokens: number;
|
||||||
output_tokens: number;
|
output_tokens: number;
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProfileOption {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
dataset_name: string;
|
||||||
|
dataset_source: string;
|
||||||
|
dataset_prompt_tokens: number;
|
||||||
|
dataset_output_tokens: number;
|
||||||
|
request_rate: number;
|
||||||
|
total_requests: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useIntl, useNavigate, useSearchParams } from '@umijs/max';
|
import { useIntl, useNavigate, useSearchParams } from '@umijs/max';
|
||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { PageContainerInner } from '../_components/page-box';
|
import { PageContainerInner } from '../_components/page-box';
|
||||||
import PageBreadcrumb from '../_components/page-breadcrumb';
|
import PageBreadcrumb from '../_components/page-breadcrumb';
|
||||||
import DetailContent from './components/detail-content';
|
import DetailContent from './components/detail-content';
|
||||||
@@ -19,6 +19,11 @@ const Details: React.FC = () => {
|
|||||||
title: name
|
title: name
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.title = `${intl.formatMessage({ id: 'benchmark.title' })} - ${name}`;
|
||||||
|
}, [name, intl]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContainerInner
|
<PageContainerInner
|
||||||
header={{
|
header={{
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ import { PageAction } from '@/config';
|
|||||||
import useAppUtils from '@/hooks/use-app-utils';
|
import useAppUtils from '@/hooks/use-app-utils';
|
||||||
import { ClusterStatusValueMap } from '@/pages/cluster-management/config';
|
import { ClusterStatusValueMap } from '@/pages/cluster-management/config';
|
||||||
import { useQueryClusterList } from '@/pages/cluster-management/services/use-query-cluster-list';
|
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 { useQueryModelInstancesList } from '@/pages/llmodels/services/use-query-model-instances';
|
||||||
import { useQueryModelList } from '@/pages/llmodels/services/use-query-model-list';
|
import { useQueryModelList } from '@/pages/llmodels/services/use-query-model-list';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
@@ -28,9 +32,9 @@ const BasicForm: React.FC = () => {
|
|||||||
} = useQueryClusterList();
|
} = useQueryClusterList();
|
||||||
const {
|
const {
|
||||||
loading: modelLoading,
|
loading: modelLoading,
|
||||||
fetchModelList,
|
fetchData: fetchModelList,
|
||||||
cancelRequest: cancelModelRequest,
|
cancelRequest: cancelModelRequest,
|
||||||
modelList
|
dataList: modelList
|
||||||
} = useQueryModelList();
|
} = useQueryModelList();
|
||||||
const {
|
const {
|
||||||
loading: instanceLoading,
|
loading: instanceLoading,
|
||||||
@@ -65,6 +69,15 @@ const BasicForm: React.FC = () => {
|
|||||||
form.setFieldValue('model_id', option?.id);
|
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(() => {
|
useEffect(() => {
|
||||||
const initClusterId = (list: any[]) => {
|
const initClusterId = (list: any[]) => {
|
||||||
// Find default cluster
|
// Find default cluster
|
||||||
@@ -163,7 +176,11 @@ const BasicForm: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<SealSelect
|
<SealSelect
|
||||||
loading={instanceLoading}
|
loading={instanceLoading}
|
||||||
options={instanceList}
|
options={instanceList.map((item) => ({
|
||||||
|
...item,
|
||||||
|
disabled: item.state !== InstanceStatusMap.Running
|
||||||
|
}))}
|
||||||
|
optionRender={optionRender}
|
||||||
onOpenChange={onInstanceOpenChange}
|
onOpenChange={onInstanceOpenChange}
|
||||||
label={intl.formatMessage({ id: 'benchmark.table.instance' })}
|
label={intl.formatMessage({ id: 'benchmark.table.instance' })}
|
||||||
required
|
required
|
||||||
|
|||||||
@@ -1,40 +1,129 @@
|
|||||||
|
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 SealInput from '@/components/seal-form/seal-input';
|
||||||
import SealSelect from '@/components/seal-form/seal-select';
|
import SealSelect from '@/components/seal-form/seal-select';
|
||||||
import useAppUtils from '@/hooks/use-app-utils';
|
import useAppUtils from '@/hooks/use-app-utils';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Form } from 'antd';
|
import { Form } from 'antd';
|
||||||
|
import _ from 'lodash';
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { profileOptions } from '../config';
|
import { ProfileValueMap } from '../config';
|
||||||
import { useFormContext } from '../config/form-context';
|
import { useFormContext } from '../config/form-context';
|
||||||
import { FormData } from '../config/types';
|
import { FormData } from '../config/types';
|
||||||
import useQueryDataset from '../services/use-query-dataset';
|
import useQueryDataset from '../services/use-query-dataset';
|
||||||
|
import useQueryProfiles from '../services/use-query-profiles';
|
||||||
import RandomSettingsForm from './random-settings';
|
import RandomSettingsForm from './random-settings';
|
||||||
|
|
||||||
|
const profileFields = [
|
||||||
|
'dataset_prompt_tokens',
|
||||||
|
'dataset_output_tokens',
|
||||||
|
'request_rate',
|
||||||
|
'total_requests'
|
||||||
|
];
|
||||||
|
|
||||||
const DatasetForm: React.FC = () => {
|
const DatasetForm: React.FC = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const form = Form.useFormInstance();
|
const form = Form.useFormInstance();
|
||||||
const { getRuleMessage } = useAppUtils();
|
const { getRuleMessage } = useAppUtils();
|
||||||
const { action, open } = useFormContext();
|
const { action, open } = useFormContext();
|
||||||
const {
|
const {
|
||||||
dataList: datasetList,
|
datasetList,
|
||||||
loading: datasetLoading,
|
loading: datasetLoading,
|
||||||
fetchData,
|
fetchDatasetData,
|
||||||
cancelRequest: cancelDatasetRequest
|
cancelRequest: cancelDatasetRequest
|
||||||
} = useQueryDataset();
|
} = useQueryDataset();
|
||||||
|
const {
|
||||||
|
profilesOptions,
|
||||||
|
fetchProfilesData,
|
||||||
|
cancelRequest: cancelProfilesRequest
|
||||||
|
} = useQueryProfiles();
|
||||||
|
const profileConfigCache = React.useRef<{ [key: string]: any }>({});
|
||||||
|
|
||||||
const handleOnDataSetChange = (value: any, option: any) => {
|
const handleOnDataSetChange = (value: any, option: any) => {
|
||||||
form.setFieldValue('dataset_id', option?.data?.id);
|
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
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
form.setFieldsValue({
|
||||||
|
profile: ProfileValueMap.Custom,
|
||||||
|
dataset_id: option?.data?.id,
|
||||||
|
dataset_prompt_tokens: option?.prompt_tokens,
|
||||||
|
dataset_output_tokens: option?.output_tokens,
|
||||||
|
request_rate: null,
|
||||||
|
total_requests: null
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnDatasetOpenChange = async (open: boolean) => {
|
const setCustomProfileValues = () => {
|
||||||
if (!datasetList.length && open) {
|
form.setFieldsValue({
|
||||||
await fetchData({ page: -1 });
|
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 = {};
|
||||||
|
} else {
|
||||||
|
const dataset_id = datasetList.find(
|
||||||
|
(item) => item.label === option.config?.dataset_name
|
||||||
|
)?.value;
|
||||||
|
|
||||||
|
form.setFieldsValue({
|
||||||
|
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(() => {
|
useEffect(() => {
|
||||||
if (!open) {
|
if (!open) {
|
||||||
cancelDatasetRequest();
|
cancelDatasetRequest();
|
||||||
|
cancelProfilesRequest();
|
||||||
|
}
|
||||||
|
if (open) {
|
||||||
|
fetchProfilesData();
|
||||||
|
fetchDatasetData();
|
||||||
}
|
}
|
||||||
}, [open]);
|
}, [open]);
|
||||||
|
|
||||||
@@ -51,7 +140,8 @@ const DatasetForm: React.FC = () => {
|
|||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<SealSelect
|
<SealSelect
|
||||||
options={profileOptions}
|
onChange={handleProfileChange}
|
||||||
|
options={profilesOptions}
|
||||||
label={intl.formatMessage({ id: 'benchmark.form.profile' })}
|
label={intl.formatMessage({ id: 'benchmark.form.profile' })}
|
||||||
required
|
required
|
||||||
></SealSelect>
|
></SealSelect>
|
||||||
@@ -65,51 +155,55 @@ const DatasetForm: React.FC = () => {
|
|||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<SealSelect
|
<AutoComplete
|
||||||
options={datasetList.map((item) => ({
|
options={datasetList.map((item) => ({
|
||||||
label: item.name,
|
...item,
|
||||||
value: item.name
|
label: item.label,
|
||||||
|
value: item.label
|
||||||
}))}
|
}))}
|
||||||
loading={datasetLoading}
|
loading={datasetLoading}
|
||||||
onChange={handleOnDataSetChange}
|
onChange={handleOnDataSetChange}
|
||||||
onOpenChange={handleOnDatasetOpenChange}
|
|
||||||
label={intl.formatMessage({ id: 'benchmark.table.dataset' })}
|
label={intl.formatMessage({ id: 'benchmark.table.dataset' })}
|
||||||
required
|
required
|
||||||
></SealSelect>
|
></AutoComplete>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item<FormData> hidden name="dataset_id">
|
<Form.Item<FormData> hidden name="dataset_id">
|
||||||
<SealInput.Input></SealInput.Input>
|
<SealInput.Input></SealInput.Input>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<RandomSettingsForm></RandomSettingsForm>
|
<RandomSettingsForm
|
||||||
|
onValueChange={handleOnProfileConfigChange}
|
||||||
|
></RandomSettingsForm>
|
||||||
<Form.Item<FormData>
|
<Form.Item<FormData>
|
||||||
name="request_rate"
|
name="request_rate"
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: getRuleMessage('select', 'benchmark.table.requestRate')
|
message: getRuleMessage('input', 'benchmark.table.requestRate')
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<SealSelect
|
<SealInputNumber
|
||||||
options={[]}
|
min={0}
|
||||||
|
onChange={handleOnProfileConfigChange}
|
||||||
label={intl.formatMessage({ id: 'benchmark.table.requestRate' })}
|
label={intl.formatMessage({ id: 'benchmark.table.requestRate' })}
|
||||||
required
|
required
|
||||||
></SealSelect>
|
></SealInputNumber>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item<FormData>
|
<Form.Item<FormData>
|
||||||
name="total_requests"
|
name="total_requests"
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: getRuleMessage('select', 'benchmark.form.totalRequests')
|
message: getRuleMessage('input', 'benchmark.form.totalRequests')
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<SealSelect
|
<SealInputNumber
|
||||||
options={[]}
|
min={0}
|
||||||
|
onChange={handleOnProfileConfigChange}
|
||||||
label={intl.formatMessage({ id: 'benchmark.form.totalRequests' })}
|
label={intl.formatMessage({ id: 'benchmark.form.totalRequests' })}
|
||||||
required
|
required
|
||||||
></SealSelect>
|
></SealInputNumber>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -99,7 +99,17 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
|
|||||||
open
|
open
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Form form={form} onFinish={onFinish} initialValues={{}}>
|
<Form
|
||||||
|
form={form}
|
||||||
|
onFinish={onFinish}
|
||||||
|
initialValues={{
|
||||||
|
dataset_prompt_tokens: null,
|
||||||
|
dataset_output_tokens: null,
|
||||||
|
total_requests: null,
|
||||||
|
request_rate: null,
|
||||||
|
seed: null
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Basic />
|
<Basic />
|
||||||
<CollapsePanel
|
<CollapsePanel
|
||||||
activeKey={activeKey}
|
activeKey={activeKey}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import SealInputNumber from '@/components/seal-form/input-number';
|
import SealInputNumber from '@/components/seal-form/input-number';
|
||||||
import SealSelect from '@/components/seal-form/seal-select';
|
|
||||||
import useAppUtils from '@/hooks/use-app-utils';
|
import useAppUtils from '@/hooks/use-app-utils';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Form } from 'antd';
|
import { Form } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FormData } from '../config/types';
|
import { FormData } from '../config/types';
|
||||||
|
|
||||||
const DatasetForm: React.FC = () => {
|
const DatasetForm: React.FC<{
|
||||||
|
onValueChange?: (value: any) => void;
|
||||||
|
}> = ({ onValueChange }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const form = Form.useFormInstance();
|
const form = Form.useFormInstance();
|
||||||
const { getRuleMessage } = useAppUtils();
|
const { getRuleMessage } = useAppUtils();
|
||||||
@@ -18,27 +19,16 @@ const DatasetForm: React.FC = () => {
|
|||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: getRuleMessage(
|
message: getRuleMessage('input', 'benchmark.table.inputTokenLength')
|
||||||
'select',
|
|
||||||
'benchmark.table.inputTokenLength'
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<SealSelect
|
<SealInputNumber
|
||||||
options={[
|
min={0}
|
||||||
{
|
|
||||||
label: '100',
|
|
||||||
value: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '128',
|
|
||||||
value: 128
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
label={intl.formatMessage({ id: 'benchmark.table.inputTokenLength' })}
|
label={intl.formatMessage({ id: 'benchmark.table.inputTokenLength' })}
|
||||||
required
|
required
|
||||||
></SealSelect>
|
onChange={onValueChange}
|
||||||
|
></SealInputNumber>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item<FormData>
|
<Form.Item<FormData>
|
||||||
name="dataset_output_tokens"
|
name="dataset_output_tokens"
|
||||||
@@ -46,33 +36,28 @@ const DatasetForm: React.FC = () => {
|
|||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: getRuleMessage(
|
message: getRuleMessage(
|
||||||
'select',
|
'input',
|
||||||
'benchmark.table.outputTokenLength'
|
'benchmark.table.outputTokenLength'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<SealSelect
|
<SealInputNumber
|
||||||
options={[
|
min={0}
|
||||||
{
|
|
||||||
label: '4',
|
|
||||||
value: 4
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '8',
|
|
||||||
value: 8
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: 'benchmark.table.outputTokenLength'
|
id: 'benchmark.table.outputTokenLength'
|
||||||
})}
|
})}
|
||||||
required
|
required
|
||||||
></SealSelect>
|
onChange={onValueChange}
|
||||||
|
></SealInputNumber>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item<FormData> name="seed">
|
<Form.Item<FormData>
|
||||||
|
name="seed"
|
||||||
|
getValueProps={(value) => ({ value: value || null })}
|
||||||
|
>
|
||||||
<SealInputNumber
|
<SealInputNumber
|
||||||
|
min={0}
|
||||||
label={intl.formatMessage({ id: 'playground.image.params.seed' })}
|
label={intl.formatMessage({ id: 'playground.image.params.seed' })}
|
||||||
required
|
|
||||||
></SealInputNumber>
|
></SealInputNumber>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { exportJsonToExcel } from '@/utils/excel-reader';
|
||||||
|
|
||||||
|
const useExportData = (params: { columns: any[] }) => {
|
||||||
|
const { columns } = params;
|
||||||
|
const colIndexMap = columns.reduce(
|
||||||
|
(map, col) => {
|
||||||
|
if (col.dataIndex !== 'operations') {
|
||||||
|
map[col.dataIndex] = col.title;
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
},
|
||||||
|
{} as Record<string, any>
|
||||||
|
);
|
||||||
|
|
||||||
|
const exportData = (dataList: any[]) => {
|
||||||
|
const fileName = `benchmark.xlsx`;
|
||||||
|
exportJsonToExcel({
|
||||||
|
jsonData: dataList || [],
|
||||||
|
fileName: fileName,
|
||||||
|
fields: Object.keys(colIndexMap),
|
||||||
|
fieldLabels: colIndexMap,
|
||||||
|
formatMap: {}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return { exportData };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useExportData;
|
||||||
@@ -4,12 +4,15 @@ import { FilterBar } from '@/components/page-tools';
|
|||||||
import { PageAction } from '@/config';
|
import { PageAction } from '@/config';
|
||||||
import { TABLE_SORT_DIRECTIONS } from '@/config/settings';
|
import { TABLE_SORT_DIRECTIONS } from '@/config/settings';
|
||||||
import useTableFetch from '@/hooks/use-table-fetch';
|
import useTableFetch from '@/hooks/use-table-fetch';
|
||||||
|
import { useQueryModelList } from '@/pages/llmodels/services/use-query-model-list';
|
||||||
import { useIntl, useNavigate } from '@umijs/max';
|
import { useIntl, useNavigate } from '@umijs/max';
|
||||||
import { useMemoizedFn } from 'ahooks';
|
import { useMemoizedFn } from 'ahooks';
|
||||||
import { ConfigProvider, Table, message } from 'antd';
|
import { ConfigProvider, Table, message } from 'antd';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
import NoResult from '../_components/no-result';
|
import NoResult from '../_components/no-result';
|
||||||
import PageBox from '../_components/page-box';
|
import PageBox from '../_components/page-box';
|
||||||
|
import useQueryGPUs from '../resources/services/use-query-gpus';
|
||||||
import {
|
import {
|
||||||
createBenchmark,
|
createBenchmark,
|
||||||
deleteBenchmark,
|
deleteBenchmark,
|
||||||
@@ -24,6 +27,7 @@ import { FormData, BenchmarkListItem as ListItem } from './config/types';
|
|||||||
import useBenchmarkColumns from './hooks/use-benchmark-columns';
|
import useBenchmarkColumns from './hooks/use-benchmark-columns';
|
||||||
import useColumnSettings from './hooks/use-column-settings';
|
import useColumnSettings from './hooks/use-column-settings';
|
||||||
import useCreateBenchmark from './hooks/use-create-benchmark';
|
import useCreateBenchmark from './hooks/use-create-benchmark';
|
||||||
|
import useExportData from './hooks/use-export-data';
|
||||||
import useViewDetail from './hooks/use-view-detail';
|
import useViewDetail from './hooks/use-view-detail';
|
||||||
|
|
||||||
const Benchmark: React.FC = () => {
|
const Benchmark: React.FC = () => {
|
||||||
@@ -38,6 +42,7 @@ const Benchmark: React.FC = () => {
|
|||||||
fetchData,
|
fetchData,
|
||||||
handlePageChange,
|
handlePageChange,
|
||||||
handleTableChange,
|
handleTableChange,
|
||||||
|
handleQueryChange,
|
||||||
handleSearch,
|
handleSearch,
|
||||||
handleNameChange
|
handleNameChange
|
||||||
} = useTableFetch<ListItem>({
|
} = useTableFetch<ListItem>({
|
||||||
@@ -54,7 +59,26 @@ const Benchmark: React.FC = () => {
|
|||||||
closeViewDetailModal,
|
closeViewDetailModal,
|
||||||
openViewDetailModalStatus
|
openViewDetailModalStatus
|
||||||
} = useViewDetail();
|
} = useViewDetail();
|
||||||
|
const { dataList: modelList, fetchData: fetchModelList } = useQueryModelList({
|
||||||
|
getValue: (item: any) => item.name
|
||||||
|
});
|
||||||
|
const { fetchData: fetchGpuList } = useQueryGPUs();
|
||||||
const { SettingsButton, selectedColumns } = useColumnSettings();
|
const { SettingsButton, selectedColumns } = useColumnSettings();
|
||||||
|
const [gpuVendorList, setGpuVendorList] = useState<
|
||||||
|
Global.BaseOption<string>[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleAddBenchmark = () => {
|
const handleAddBenchmark = () => {
|
||||||
openBenchmarkModal(PageAction.CREATE, 'Add Benchmark');
|
openBenchmarkModal(PageAction.CREATE, 'Add Benchmark');
|
||||||
@@ -137,6 +161,15 @@ const Benchmark: React.FC = () => {
|
|||||||
handleOnCellClick
|
handleOnCellClick
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { exportData } = useExportData({ columns: columns });
|
||||||
|
|
||||||
|
const handleExportData = () => {
|
||||||
|
const list = dataSource.dataList.filter((item) =>
|
||||||
|
rowSelection.selectedRowKeys.includes(item.id)
|
||||||
|
);
|
||||||
|
exportData(list);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageBox>
|
<PageBox>
|
||||||
@@ -150,7 +183,10 @@ const Benchmark: React.FC = () => {
|
|||||||
widths={{ input: 300 }}
|
widths={{ input: 300 }}
|
||||||
left={
|
left={
|
||||||
<LeftActions
|
<LeftActions
|
||||||
|
modelList={modelList}
|
||||||
|
gpuVendorList={gpuVendorList}
|
||||||
handleSearch={handleSearch}
|
handleSearch={handleSearch}
|
||||||
|
handleQueryChange={handleQueryChange}
|
||||||
handleInputChange={handleNameChange}
|
handleInputChange={handleNameChange}
|
||||||
></LeftActions>
|
></LeftActions>
|
||||||
}
|
}
|
||||||
@@ -159,6 +195,7 @@ const Benchmark: React.FC = () => {
|
|||||||
settingButton={SettingsButton}
|
settingButton={SettingsButton}
|
||||||
handleDeleteByBatch={handleDeleteBatch}
|
handleDeleteByBatch={handleDeleteBatch}
|
||||||
handleClickPrimary={handleAddBenchmark}
|
handleClickPrimary={handleAddBenchmark}
|
||||||
|
handleExport={handleExportData}
|
||||||
buttonText={intl.formatMessage({
|
buttonText={intl.formatMessage({
|
||||||
id: 'benchmark.button.add'
|
id: 'benchmark.button.add'
|
||||||
})}
|
})}
|
||||||
@@ -176,6 +213,7 @@ const Benchmark: React.FC = () => {
|
|||||||
sortDirections={TABLE_SORT_DIRECTIONS}
|
sortDirections={TABLE_SORT_DIRECTIONS}
|
||||||
showSorterTooltip={false}
|
showSorterTooltip={false}
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
|
scroll={{ x: 1200 }}
|
||||||
onChange={handleTableChange}
|
onChange={handleTableChange}
|
||||||
pagination={{
|
pagination={{
|
||||||
showSizeChanger: true,
|
showSizeChanger: true,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useQueryDataList } from '@/hooks/use-query-data-list';
|
import { useQueryDataList } from '@/hooks/use-query-data-list';
|
||||||
|
import { useState } from 'react';
|
||||||
import { queryDatasetList } from '../apis';
|
import { queryDatasetList } from '../apis';
|
||||||
import { DatasetListItem } from '../config/types';
|
import { DatasetListItem } from '../config/types';
|
||||||
|
|
||||||
@@ -11,10 +12,34 @@ const useQueryDataset = () => {
|
|||||||
fetchList: queryDatasetList
|
fetchList: queryDatasetList
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [datasetList, setDatasetList] = useState<
|
||||||
|
Global.BaseOption<number | string>[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
|
const fetchDatasetData = async () => {
|
||||||
|
const items = await fetchData({
|
||||||
|
page: -1
|
||||||
|
});
|
||||||
|
const list =
|
||||||
|
items?.map((item) => ({
|
||||||
|
...item,
|
||||||
|
label: item.name,
|
||||||
|
value: item.id
|
||||||
|
})) || [];
|
||||||
|
|
||||||
|
setDatasetList([
|
||||||
|
...list,
|
||||||
|
{
|
||||||
|
label: 'Custom',
|
||||||
|
value: 'Custom'
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dataList,
|
datasetList,
|
||||||
loading,
|
loading,
|
||||||
fetchData,
|
fetchDatasetData,
|
||||||
cancelRequest
|
cancelRequest
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
import { useQueryData } from '@/hooks/use-query-data-list';
|
|
||||||
import { queryBenchmarkMetrics } from '../apis';
|
|
||||||
import { BenchmarkMetricsFormData } from '../config/detail-types';
|
|
||||||
|
|
||||||
export default function useQueryBenchmarkMetrics() {
|
|
||||||
const { detailData, loading, cancelRequest, fetchData } = useQueryData<
|
|
||||||
any,
|
|
||||||
{
|
|
||||||
id: number;
|
|
||||||
data: BenchmarkMetricsFormData;
|
|
||||||
}
|
|
||||||
>({
|
|
||||||
fetchDetail: queryBenchmarkMetrics,
|
|
||||||
key: 'benchmarkMetrics'
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
detailData,
|
|
||||||
loading,
|
|
||||||
cancelRequest,
|
|
||||||
fetchData
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import { useQueryData } from '@/hooks/use-query-data-list';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { queryProfiles } from '../apis';
|
||||||
|
import { ProfileOption } from '../config/types';
|
||||||
|
|
||||||
|
export default function useQueryProfiles() {
|
||||||
|
const { detailData, loading, cancelRequest, fetchData } = useQueryData<{
|
||||||
|
profiles: ProfileOption[];
|
||||||
|
}>({
|
||||||
|
fetchDetail: queryProfiles,
|
||||||
|
key: 'profiles'
|
||||||
|
});
|
||||||
|
const [profilesOptions, setProfilesOptions] = useState<
|
||||||
|
{
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
config: Partial<ProfileOption>;
|
||||||
|
}[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
|
const fetchProfilesData = async () => {
|
||||||
|
const res = await fetchData({});
|
||||||
|
const list =
|
||||||
|
res?.profiles?.map((item) => {
|
||||||
|
return {
|
||||||
|
label: item.name,
|
||||||
|
value: item.name,
|
||||||
|
config: {
|
||||||
|
..._.omit(item, 'name')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}) || [];
|
||||||
|
|
||||||
|
setProfilesOptions([
|
||||||
|
...list,
|
||||||
|
{
|
||||||
|
label: 'Custom',
|
||||||
|
value: 'Custom',
|
||||||
|
config: {
|
||||||
|
dataset_name: '',
|
||||||
|
dataset_prompt_tokens: null,
|
||||||
|
dataset_output_tokens: null,
|
||||||
|
request_rate: null,
|
||||||
|
total_requests: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
profilesOptions,
|
||||||
|
loading,
|
||||||
|
cancelRequest,
|
||||||
|
fetchProfilesData
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,67 +1,25 @@
|
|||||||
import { createAxiosToken } from '@/hooks/use-chunk-request';
|
import { useQueryDataList } from '@/hooks/use-query-data-list';
|
||||||
import { useRequest } from 'ahooks';
|
|
||||||
import { message } from 'antd';
|
|
||||||
import { CancelTokenSource } from 'axios';
|
|
||||||
import { useEffect, useRef, useState } from 'react';
|
|
||||||
import { queryModelsList } from '../apis';
|
import { queryModelsList } from '../apis';
|
||||||
import { ListItem } from '../config/types';
|
import { ListItem } from '../config/types';
|
||||||
|
|
||||||
/**
|
export const useQueryModelList = (optons?: {
|
||||||
*
|
getLabel?: (item: ListItem) => string;
|
||||||
* @returns loading, fetch, dataList
|
getValue?: (item: ListItem) => any;
|
||||||
*/
|
}) => {
|
||||||
export const useQueryModelList = () => {
|
const { dataList, loading, fetchData, cancelRequest } = useQueryDataList<
|
||||||
const axiosTokenRef = useRef<CancelTokenSource | null>(null);
|
ListItem,
|
||||||
const [dataList, setDataList] = useState<
|
Global.SearchParams
|
||||||
Array<Partial<ListItem> & { label: string; value: number }>
|
>({
|
||||||
>([]);
|
key: 'modelList',
|
||||||
|
fetchList: queryModelsList
|
||||||
const {
|
});
|
||||||
runAsync: fetchData,
|
|
||||||
loading,
|
|
||||||
cancel
|
|
||||||
} = useRequest(
|
|
||||||
async (params: { page: number; perPage?: number; cluster_id?: number }) => {
|
|
||||||
axiosTokenRef.current?.cancel();
|
|
||||||
axiosTokenRef.current = createAxiosToken();
|
|
||||||
const res = await queryModelsList(params, {
|
|
||||||
token: axiosTokenRef.current.token
|
|
||||||
});
|
|
||||||
setDataList(
|
|
||||||
res.items?.map((item: ListItem) => ({
|
|
||||||
...item,
|
|
||||||
label: item.name,
|
|
||||||
value: item.id
|
|
||||||
})) || []
|
|
||||||
);
|
|
||||||
return res.items || [];
|
|
||||||
},
|
|
||||||
{
|
|
||||||
manual: true,
|
|
||||||
onSuccess: (response) => {},
|
|
||||||
onError: (error) => {
|
|
||||||
message.error(error?.message || 'Failed to fetch model list');
|
|
||||||
setDataList([]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const cancelRequest = () => {
|
|
||||||
cancel();
|
|
||||||
axiosTokenRef.current?.cancel();
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
cancel();
|
|
||||||
axiosTokenRef.current?.cancel();
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
dataList,
|
||||||
loading,
|
loading,
|
||||||
modelList: dataList,
|
fetchData,
|
||||||
cancelRequest,
|
cancelRequest
|
||||||
fetchModelList: fetchData
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default useQueryModelList;
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { useQueryDataList } from '@/hooks/use-query-data-list';
|
||||||
|
import { queryGpuDevicesList } from '../apis';
|
||||||
|
import { GPUDeviceItem as ListItem } from '../config/types';
|
||||||
|
|
||||||
|
export const useQueryGPUs = (optons?: {
|
||||||
|
getLabel?: (item: ListItem) => string;
|
||||||
|
getValue?: (item: ListItem) => any;
|
||||||
|
}) => {
|
||||||
|
const { dataList, loading, fetchData, cancelRequest } = useQueryDataList<
|
||||||
|
ListItem,
|
||||||
|
Global.SearchParams
|
||||||
|
>({
|
||||||
|
key: 'gpuList',
|
||||||
|
fetchList: queryGpuDevicesList
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
dataList,
|
||||||
|
loading,
|
||||||
|
fetchData,
|
||||||
|
cancelRequest
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useQueryGPUs;
|
||||||
Reference in New Issue
Block a user