refactor: summary
This commit is contained in:
@@ -0,0 +1,57 @@
|
|||||||
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
interface CollapseProps {
|
||||||
|
open: boolean;
|
||||||
|
children: React.ReactNode;
|
||||||
|
duration?: number;
|
||||||
|
minHeight?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Collapse({
|
||||||
|
open,
|
||||||
|
children,
|
||||||
|
minHeight = 0,
|
||||||
|
duration = 200
|
||||||
|
}: CollapseProps) {
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
const [height, setHeight] = useState<number | 'auto'>(minHeight);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const el = ref.current;
|
||||||
|
if (!el) return;
|
||||||
|
|
||||||
|
if (open) {
|
||||||
|
const h = el.scrollHeight;
|
||||||
|
setHeight(h);
|
||||||
|
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
setHeight('auto');
|
||||||
|
}, duration);
|
||||||
|
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
} else {
|
||||||
|
const h = el.scrollHeight;
|
||||||
|
setHeight(h);
|
||||||
|
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
setHeight(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}, [open, duration]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
style={{
|
||||||
|
height,
|
||||||
|
overflow: 'hidden',
|
||||||
|
transition: `height ${duration}ms ease`
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
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 { detailData } = useDetailContext();
|
|
||||||
return (
|
|
||||||
<Container>
|
|
||||||
<Instance />
|
|
||||||
<Benchmark />
|
|
||||||
</Container>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Configure;
|
|
||||||
@@ -6,7 +6,6 @@ import React, { useEffect, useState } from 'react';
|
|||||||
import DetailContext from '../config/detail-context';
|
import DetailContext from '../config/detail-context';
|
||||||
import { BenchmarkListItem } from '../config/types';
|
import { BenchmarkListItem } from '../config/types';
|
||||||
import useQueryDetail from '../services/use-query-detail';
|
import useQueryDetail from '../services/use-query-detail';
|
||||||
import Configure from './configure';
|
|
||||||
import Environment from './environment';
|
import Environment from './environment';
|
||||||
import Summary from './summary';
|
import Summary from './summary';
|
||||||
|
|
||||||
@@ -31,12 +30,6 @@ const Details: React.FC<{ currentData?: BenchmarkListItem }> = ({
|
|||||||
children: <Summary />,
|
children: <Summary />,
|
||||||
icon: <IconFont type="icon-basic" />
|
icon: <IconFont type="icon-basic" />
|
||||||
},
|
},
|
||||||
{
|
|
||||||
key: 'configure',
|
|
||||||
label: intl.formatMessage({ id: 'benchmark.detail.configure.title' }),
|
|
||||||
children: <Configure />,
|
|
||||||
icon: <IconFont type="icon-settings-02" />
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
key: 'environment',
|
key: 'environment',
|
||||||
label: intl.formatMessage({ id: 'benchmark.detail.environment.title' }),
|
label: intl.formatMessage({ id: 'benchmark.detail.environment.title' }),
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import _, { round } from 'lodash';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { useDetailContext } from '../../config/detail-context';
|
import { useDetailContext } from '../../config/detail-context';
|
||||||
import PercentileResult from './percentile-result';
|
|
||||||
import Section from './section';
|
import Section from './section';
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
@@ -239,7 +238,6 @@ const Summary: React.FC = () => {
|
|||||||
></Descriptions>
|
></Descriptions>
|
||||||
</Section>
|
</Section>
|
||||||
</Box>
|
</Box>
|
||||||
<PercentileResult />
|
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
import { Descriptions, DescriptionsProps } from 'antd';
|
import { Descriptions, DescriptionsProps } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useDetailContext } from '../../config/detail-context';
|
import { useDetailContext } from '../../config/detail-context';
|
||||||
import Section from '../summary/section';
|
import Section from './section';
|
||||||
|
|
||||||
const Benchmark: React.FC = () => {
|
const Benchmark: React.FC = () => {
|
||||||
const { detailData } = useDetailContext();
|
const { detailData } = useDetailContext();
|
||||||
@@ -19,7 +19,7 @@ const Benchmark: React.FC = () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: '3',
|
key: '3',
|
||||||
label: 'Token Length (In/Out)',
|
label: 'Token Length (Prompt/Out)',
|
||||||
children: (
|
children: (
|
||||||
<span>
|
<span>
|
||||||
{detailData?.dataset_prompt_tokens || '-'} /{' '}
|
{detailData?.dataset_prompt_tokens || '-'} /{' '}
|
||||||
@@ -1,19 +1,23 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import BasicInfo from './basic';
|
import BenchMark from './benchmark';
|
||||||
import MetricsInfo from './metrics';
|
import Instance from './instance';
|
||||||
|
import MetricsResult from './metrics-result';
|
||||||
|
import PercentileResult from './percentile-result';
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 24px;
|
gap: 16px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Summary: React.FC = () => {
|
const Summary: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<BasicInfo />
|
<Instance />
|
||||||
<MetricsInfo />
|
<BenchMark />
|
||||||
|
<MetricsResult />
|
||||||
|
<PercentileResult />
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+10
-42
@@ -1,11 +1,8 @@
|
|||||||
import StatusTag from '@/components/status-tag';
|
|
||||||
import { InstanceStatusMapValue, status } from '@/pages/llmodels/config';
|
|
||||||
import { convertFileSize } from '@/utils';
|
|
||||||
import { Descriptions, Flex, Tag } from 'antd';
|
import { Descriptions, Flex, Tag } from 'antd';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { useDetailContext } from '../../config/detail-context';
|
import { useDetailContext } from '../../config/detail-context';
|
||||||
import Section from '../summary/section';
|
import Section from './section';
|
||||||
|
|
||||||
const calcTotalVram = (vram: Record<string, number>) => {
|
const calcTotalVram = (vram: Record<string, number>) => {
|
||||||
return _.sum(_.values(vram));
|
return _.sum(_.values(vram));
|
||||||
@@ -17,21 +14,16 @@ const Instance: React.FC = () => {
|
|||||||
const items = useMemo(() => {
|
const items = useMemo(() => {
|
||||||
const { snapshot } = detailData;
|
const { snapshot } = detailData;
|
||||||
const [instanceName, instanceData] =
|
const [instanceName, instanceData] =
|
||||||
Object.entries(snapshot.instances || {})[0] || [];
|
Object.entries(snapshot?.instances || {})[0] || [];
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
key: '1',
|
key: '1',
|
||||||
label: 'Instance Name',
|
label: 'Instance Name',
|
||||||
children: (
|
children: (
|
||||||
<div className="flex-center gap-8">
|
<div className="flex-center gap-8">
|
||||||
<span>{instanceName}</span>
|
<span>
|
||||||
<StatusTag
|
{detailData?.model_name}/{detailData?.model_instance_name}
|
||||||
statusValue={{
|
</span>
|
||||||
status: status[instanceData.state],
|
|
||||||
text: InstanceStatusMapValue[instanceData.state],
|
|
||||||
message: detailData.state_message || undefined
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -39,42 +31,18 @@ const Instance: React.FC = () => {
|
|||||||
key: '2',
|
key: '2',
|
||||||
label: 'Worker',
|
label: 'Worker',
|
||||||
children: instanceData?.worker_name || '-'
|
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',
|
key: '6',
|
||||||
label: 'GPU Type',
|
label: 'GPU Type',
|
||||||
children: instanceData?.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',
|
key: '5',
|
||||||
label: 'Backend',
|
label: 'Backend',
|
||||||
children: `${instanceData?.backend || '-'} ${
|
children: `${instanceData?.backend || '-'} ${
|
||||||
instanceData.backend_version
|
instanceData?.backend_version
|
||||||
? `(${instanceData.backend_version})`
|
? `(${instanceData?.backend_version})`
|
||||||
: ''
|
: ''
|
||||||
}`
|
}`
|
||||||
}
|
}
|
||||||
@@ -84,14 +52,14 @@ const Instance: React.FC = () => {
|
|||||||
const paramsItems = useMemo(() => {
|
const paramsItems = useMemo(() => {
|
||||||
const { snapshot } = detailData;
|
const { snapshot } = detailData;
|
||||||
const [instanceName, instanceData] =
|
const [instanceName, instanceData] =
|
||||||
Object.entries(snapshot.instances || {})[0] || [];
|
Object.entries(snapshot?.instances || {})[0] || [];
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
key: '1',
|
key: '1',
|
||||||
label: 'Backend Parameters',
|
label: 'Backend Parameters',
|
||||||
children: (
|
children: (
|
||||||
<Flex gap={8} wrap="wrap">
|
<Flex gap={8} wrap="wrap">
|
||||||
{instanceData?.backend_parameters.map(
|
{instanceData?.backend_parameters?.map(
|
||||||
(param: string, index: number) => (
|
(param: string, index: number) => (
|
||||||
<Tag key={index} style={{ margin: 0 }}>
|
<Tag key={index} style={{ margin: 0 }}>
|
||||||
{param}
|
{param}
|
||||||
@@ -180,7 +148,7 @@ const Instance: React.FC = () => {
|
|||||||
<Descriptions
|
<Descriptions
|
||||||
items={items}
|
items={items}
|
||||||
colon={false}
|
colon={false}
|
||||||
column={3}
|
column={4}
|
||||||
layout="vertical"
|
layout="vertical"
|
||||||
styles={{
|
styles={{
|
||||||
content: {
|
content: {
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
import { Descriptions } from 'antd';
|
||||||
|
import _, { round } from 'lodash';
|
||||||
|
import React from 'react';
|
||||||
|
import { useDetailContext } from '../../config/detail-context';
|
||||||
|
import Section from './section';
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: 'Duration (s)',
|
||||||
|
dataIndex: 'duration',
|
||||||
|
path: ['raw_metrics', 'benchmarks', '0', 'duration'],
|
||||||
|
unit: 's',
|
||||||
|
render: (value: number) => round(value, 2)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Concurrency',
|
||||||
|
dataIndex: 'request_concurrency',
|
||||||
|
path: ['raw_metrics', 'benchmarks', '0'],
|
||||||
|
unit: '',
|
||||||
|
render: (value: number) => {
|
||||||
|
return round(
|
||||||
|
_.get(value, 'metrics.request_concurrency.successful.mean'),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Total Requests',
|
||||||
|
dataIndex: 'total_requests',
|
||||||
|
path: 'total_requests',
|
||||||
|
unit: '',
|
||||||
|
render: (value: number) => round(value, 0)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Success Requests',
|
||||||
|
dataIndex: 'successful_requests',
|
||||||
|
path: ['raw_metrics', 'benchmarks', '0'],
|
||||||
|
unit: '',
|
||||||
|
render: (value: number) => {
|
||||||
|
return (
|
||||||
|
<span style={{ color: 'var(--ant-color-success)' }}>
|
||||||
|
{_.get(value, 'metrics.request_totals.successful')}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Failed Requests',
|
||||||
|
dataIndex: 'failed_requests',
|
||||||
|
path: ['raw_metrics', 'benchmarks', '0'],
|
||||||
|
unit: '',
|
||||||
|
render: (value: number) => {
|
||||||
|
return (
|
||||||
|
<span style={{ color: 'var(--ant-color-error)' }}>
|
||||||
|
{_.get(value, 'metrics.request_totals.errored')}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Output token throughput (t/s)',
|
||||||
|
dataIndex: 'output_tokens_per_second_mean',
|
||||||
|
path: 'output_tokens_per_second_mean',
|
||||||
|
unit: 't/s',
|
||||||
|
render: (value: number) => round(value, 2)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const columnsSub = [
|
||||||
|
{
|
||||||
|
title: 'Total token throughput (t/s)',
|
||||||
|
dataIndex: 'tokens_per_second_mean',
|
||||||
|
path: 'tokens_per_second_mean',
|
||||||
|
unit: 't/s',
|
||||||
|
render: (value: number) => round(value, 2)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Request token throughput (t/s)',
|
||||||
|
dataIndex: 'prompt_tokens_per_second_mean',
|
||||||
|
path: 'prompt_tokens_per_second_mean',
|
||||||
|
unit: 't/s',
|
||||||
|
render: (value: number) => round(value, 2)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Average Request Latency (ms)',
|
||||||
|
dataIndex: 'request_latency_mean',
|
||||||
|
path: 'request_latency_mean',
|
||||||
|
unit: 'ms',
|
||||||
|
render: (value: number) => round(value, 2)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Average Time To First Token (ms)',
|
||||||
|
dataIndex: 'time_to_first_token_mean',
|
||||||
|
path: 'time_to_first_token_mean',
|
||||||
|
unit: 'ms',
|
||||||
|
render: (value: number) => round(value, 2)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Average Time Per Output Token (ms)',
|
||||||
|
dataIndex: 'time_per_output_token_mean',
|
||||||
|
path: 'time_per_output_token_mean',
|
||||||
|
unit: 'ms',
|
||||||
|
render: (value: number) => round(value, 2)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
dataIndex: '',
|
||||||
|
path: '',
|
||||||
|
unit: '',
|
||||||
|
render: (_: number) => ''
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const PERCENTILES = [
|
||||||
|
{ key: 'metrics', label: 'Metrics', title: 'N/A' }
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
const PercentileResult: React.FC = () => {
|
||||||
|
const { detailData } = useDetailContext();
|
||||||
|
const metrics = detailData?.raw_metrics?.benchmarks?.[0]?.metrics || {};
|
||||||
|
|
||||||
|
const buildPercentileTable = (metrics: any) => {
|
||||||
|
return PERCENTILES.map(({ key, title }) => {
|
||||||
|
const row: any = { metrics: title };
|
||||||
|
|
||||||
|
[...columns, ...columnsSub].forEach(({ dataIndex, path }) => {
|
||||||
|
row[dataIndex] = _.get(detailData, path) ?? 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
return row;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const items = [...columns, ...columnsSub].map(
|
||||||
|
({ title, dataIndex, path, render, unit }) => ({
|
||||||
|
key: dataIndex,
|
||||||
|
label: title,
|
||||||
|
children: unit
|
||||||
|
? render(_.get(detailData, path) ?? 0) + ` (${unit})`
|
||||||
|
: render(_.get(detailData, path) ?? 0)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Section title="Metrics Result" minHeight={200}>
|
||||||
|
{/* <Table
|
||||||
|
size="small"
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
title: 'Metrics',
|
||||||
|
dataIndex: 'metrics'
|
||||||
|
},
|
||||||
|
...columns
|
||||||
|
]}
|
||||||
|
dataSource={buildPercentileTable(metrics)}
|
||||||
|
rowKey="percentile"
|
||||||
|
pagination={false}
|
||||||
|
styles={{
|
||||||
|
body: {
|
||||||
|
cell: {
|
||||||
|
height: 54
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
></Table> */}
|
||||||
|
<Descriptions
|
||||||
|
items={items}
|
||||||
|
colon={false}
|
||||||
|
column={2}
|
||||||
|
styles={{
|
||||||
|
content: {
|
||||||
|
justifyContent: 'flex-end'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
></Descriptions>
|
||||||
|
</Section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PercentileResult;
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { useDetailContext } from '../../config/detail-context';
|
|
||||||
|
|
||||||
const fields = [
|
|
||||||
'requests_per_second_mean',
|
|
||||||
'request_latency_mean',
|
|
||||||
'time_per_output_token_mean',
|
|
||||||
'inter_token_latency_mean',
|
|
||||||
'time_to_first_token_mean',
|
|
||||||
'tokens_per_second_mean',
|
|
||||||
'output_tokens_per_second_mean',
|
|
||||||
'prompt_tokens_per_second_mean'
|
|
||||||
];
|
|
||||||
|
|
||||||
const Summary: React.FC = () => {
|
|
||||||
const { detailData } = useDetailContext();
|
|
||||||
return <div></div>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Summary;
|
|
||||||
@@ -1,107 +1,92 @@
|
|||||||
import { Table } from 'antd';
|
import { Table } from 'antd';
|
||||||
|
import { round } from 'lodash';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useDetailContext } from '../../config/detail-context';
|
import { useDetailContext } from '../../config/detail-context';
|
||||||
import Section from './section';
|
import Section from './section';
|
||||||
|
|
||||||
// Define table columns: Percentile、 TTFT (ms) 、 ITL (ms) 、TPOT (ms)、Latency (ms)、Input Tokens (t/s)、 Output Tokens (t/s)、 Output (t/s)、Total (t/s)
|
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
|
||||||
title: 'Percentile',
|
|
||||||
dataIndex: 'percentile',
|
|
||||||
key: 'percentile'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: 'TTFT (ms)',
|
title: 'TTFT (ms)',
|
||||||
dataIndex: 'ttft',
|
dataIndex: 'time_to_first_token_ms',
|
||||||
key: 'ttft'
|
render: (value: number) => round(value, 2)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'ITL (ms)',
|
title: 'ITL (ms)',
|
||||||
dataIndex: 'itl',
|
dataIndex: 'inter_token_latency_ms',
|
||||||
key: 'itl'
|
render: (value: number) => round(value, 2)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'TPOT (ms)',
|
title: 'TPOT (ms)',
|
||||||
dataIndex: 'tpot',
|
dataIndex: 'time_per_output_token_ms',
|
||||||
key: 'tpot'
|
render: (value: number) => round(value, 2)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Latency (ms)',
|
title: 'Latency (s)',
|
||||||
dataIndex: 'latency',
|
dataIndex: 'request_latency',
|
||||||
key: 'latency'
|
render: (value: number) => round(value, 2)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Input Tokens (t/s)',
|
title: 'Input tokens',
|
||||||
dataIndex: 'inputTokens',
|
dataIndex: 'prompt_token_count',
|
||||||
key: 'inputTokens'
|
render: (value: number) => round(value, 0)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Output Tokens (t/s)',
|
title: 'Output tokens',
|
||||||
dataIndex: 'outputTokens',
|
dataIndex: 'output_token_count',
|
||||||
key: 'outputTokens'
|
render: (value: number) => round(value, 0)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Input (t/s)',
|
||||||
|
dataIndex: 'prompt_tokens_per_second',
|
||||||
|
render: (value: number) => round(value, 2)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Output (t/s)',
|
title: 'Output (t/s)',
|
||||||
dataIndex: 'output',
|
dataIndex: 'output_tokens_per_second',
|
||||||
key: 'output'
|
render: (value: number) => round(value, 2)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Total (t/s)',
|
title: 'Total (t/s)',
|
||||||
dataIndex: 'total',
|
dataIndex: 'tokens_per_second',
|
||||||
key: 'total'
|
render: (value: number) => round(value, 2)
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
// mock data example: three rows of percentiles: 50th, 90th, 99th
|
const PERCENTILES = [
|
||||||
const data = [
|
{ key: 'p50', label: '50%' },
|
||||||
{
|
{ key: 'p90', label: '90%' },
|
||||||
key: '1',
|
{ key: 'p99', label: '99%' }
|
||||||
percentile: '50%',
|
] as const;
|
||||||
ttft: 100,
|
|
||||||
itl: 50,
|
|
||||||
tpot: 20,
|
|
||||||
latency: 200,
|
|
||||||
inputTokens: 300,
|
|
||||||
outputTokens: 400,
|
|
||||||
output: 500,
|
|
||||||
total: 600
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '2',
|
|
||||||
percentile: '90%',
|
|
||||||
ttft: 150,
|
|
||||||
itl: 70,
|
|
||||||
tpot: 30,
|
|
||||||
latency: 250,
|
|
||||||
inputTokens: 350,
|
|
||||||
outputTokens: 450,
|
|
||||||
output: 550,
|
|
||||||
total: 650
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '3',
|
|
||||||
percentile: '99%',
|
|
||||||
ttft: 200,
|
|
||||||
itl: 90,
|
|
||||||
tpot: 40,
|
|
||||||
latency: 300,
|
|
||||||
inputTokens: 400,
|
|
||||||
outputTokens: 500,
|
|
||||||
output: 600,
|
|
||||||
total: 700
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const PercentileResult: React.FC = () => {
|
const PercentileResult: React.FC = () => {
|
||||||
const { detailData, id } = useDetailContext();
|
const { detailData } = useDetailContext();
|
||||||
|
const metrics = detailData?.raw_metrics?.benchmarks?.[0]?.metrics || {};
|
||||||
|
|
||||||
|
const buildPercentileTable = (metrics: any) => {
|
||||||
|
return PERCENTILES.map(({ key, label }) => {
|
||||||
|
const row: any = { percentile: label };
|
||||||
|
|
||||||
|
columns.forEach(({ dataIndex }) => {
|
||||||
|
row[dataIndex] =
|
||||||
|
metrics?.[dataIndex]?.successful?.percentiles?.[key] ?? 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
return row;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Section title="Percentiles Result">
|
<Section title="Percentiles Result">
|
||||||
<Table
|
<Table
|
||||||
size="small"
|
size="small"
|
||||||
columns={columns}
|
columns={[
|
||||||
dataSource={data}
|
{
|
||||||
|
title: 'Percentile',
|
||||||
|
dataIndex: 'percentile'
|
||||||
|
},
|
||||||
|
...columns
|
||||||
|
]}
|
||||||
|
dataSource={buildPercentileTable(metrics)}
|
||||||
rowKey="percentile"
|
rowKey="percentile"
|
||||||
pagination={false}
|
pagination={false}
|
||||||
styles={{
|
styles={{
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import HeadlessCollapse from '@/components/collapse-container/headless-collapse';
|
||||||
|
import IconFont from '@/components/icon-font';
|
||||||
|
import { useState } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
@@ -21,9 +24,10 @@ const Container = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const Title = styled.div`
|
const Title = styled.div`
|
||||||
|
justify-content: space-between;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
cursor: pointer;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
// margin-bottom: 12px;
|
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
@@ -35,16 +39,31 @@ const Title = styled.div`
|
|||||||
const DetailSection: React.FC<{
|
const DetailSection: React.FC<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
title?: React.ReactNode;
|
title?: React.ReactNode;
|
||||||
|
minHeight?: number;
|
||||||
styles?: {
|
styles?: {
|
||||||
wraper?: React.CSSProperties;
|
wraper?: React.CSSProperties;
|
||||||
container?: React.CSSProperties;
|
container?: React.CSSProperties;
|
||||||
title?: React.CSSProperties;
|
title?: React.CSSProperties;
|
||||||
};
|
};
|
||||||
}> = ({ children, title, styles }) => {
|
}> = ({ children, title, styles, minHeight = 100 }) => {
|
||||||
|
const [open, setOpen] = useState(true);
|
||||||
return (
|
return (
|
||||||
<Wrapper style={styles?.wraper}>
|
<Wrapper style={styles?.wraper}>
|
||||||
{title && <Title style={styles?.title}>{title}</Title>}
|
{title && (
|
||||||
<Container style={styles?.container}>{children}</Container>
|
<Title style={styles?.title} onClick={() => setOpen(!open)}>
|
||||||
|
{title}
|
||||||
|
<IconFont
|
||||||
|
type={'icon-down'}
|
||||||
|
rotate={open ? 0 : -90}
|
||||||
|
style={{
|
||||||
|
transition: 'transform 0.2s'
|
||||||
|
}}
|
||||||
|
></IconFont>
|
||||||
|
</Title>
|
||||||
|
)}
|
||||||
|
<HeadlessCollapse open={open} minHeight={minHeight}>
|
||||||
|
<Container style={styles?.container}>{children}</Container>
|
||||||
|
</HeadlessCollapse>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -67,6 +67,12 @@ export interface GPUData {
|
|||||||
export interface BenchmarkDetail {
|
export interface BenchmarkDetail {
|
||||||
profile: string;
|
profile: string;
|
||||||
seed: number;
|
seed: number;
|
||||||
|
raw_metrics: {
|
||||||
|
benchmarks: Array<{
|
||||||
|
metrics: Record<string, any>;
|
||||||
|
}>;
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
requests_per_second_mean: number;
|
requests_per_second_mean: number;
|
||||||
request_latency_mean: number;
|
request_latency_mean: number;
|
||||||
time_per_output_token_mean: number;
|
time_per_output_token_mean: number;
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ const ModelInstanceForm: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<SealCascader
|
<SealCascader
|
||||||
required
|
required
|
||||||
|
showSearch
|
||||||
loading={modelLoading || instanceLoading}
|
loading={modelLoading || instanceLoading}
|
||||||
changeOnSelect={false}
|
changeOnSelect={false}
|
||||||
expandTrigger="hover"
|
expandTrigger="hover"
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { useEffect } 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 {
|
import {
|
||||||
|
BENCHMARKS_API,
|
||||||
createBenchmark,
|
createBenchmark,
|
||||||
deleteBenchmark,
|
deleteBenchmark,
|
||||||
queryBenchmarkList,
|
queryBenchmarkList,
|
||||||
@@ -48,6 +49,8 @@ const Benchmark: React.FC = () => {
|
|||||||
} = useTableFetch<ListItem>({
|
} = useTableFetch<ListItem>({
|
||||||
fetchAPI: queryBenchmarkList,
|
fetchAPI: queryBenchmarkList,
|
||||||
deleteAPI: deleteBenchmark,
|
deleteAPI: deleteBenchmark,
|
||||||
|
API: BENCHMARKS_API,
|
||||||
|
watch: true,
|
||||||
contentForDelete: 'menu.models.benchmark'
|
contentForDelete: 'menu.models.benchmark'
|
||||||
});
|
});
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|||||||
Reference in New Issue
Block a user