fix: benchmark detail styles

This commit is contained in:
jialin
2026-01-30 18:48:55 +08:00
parent d173e2d49b
commit 3df2d23bf5
24 changed files with 310 additions and 218 deletions
+14 -2
View File
@@ -19,10 +19,21 @@ export const OverlayScroller: React.FC<
OverlayScrollerOptions & {
maxHeight?: number;
style?: React.CSSProperties;
styles?: {
wrapper?: React.CSSProperties;
};
children: React.ReactNode;
onScroll?: (e: React.UIEvent<HTMLDivElement, UIEvent>) => void;
}
> = ({ children, maxHeight, scrollbars, oppositeTheme, style, onScroll }) => {
> = ({
children,
maxHeight,
scrollbars,
oppositeTheme,
style,
styles,
onScroll
}) => {
const scroller = React.useRef<any>(null);
const { initialize } = useOverlayScroller({
options: {
@@ -48,7 +59,8 @@ export const OverlayScroller: React.FC<
style={{
paddingInlineStart: 8,
paddingInlineEnd: 8,
...style
...style,
...styles?.wrapper
}}
>
{children}
+1 -1
View File
@@ -21,7 +21,7 @@ export default {
'benchmark.form.profile.longContext': 'Long Context',
'benchmark.form.profile.heavy': 'Generation Heavy',
'benchmark.form.profile.custom': 'Custom',
'benchmark.table.inputTokenLength': 'Prompt Token Length',
'benchmark.table.inputTokenLength': 'Input Token Length',
'benchmark.table.outputTokenLength': 'Output Token Length',
'benchmark.detail.summary.title': 'Summary',
'benchmark.detail.configure.title': 'Configure',
+1 -1
View File
@@ -21,7 +21,7 @@ export default {
'benchmark.form.profile.longContext': 'Long Context',
'benchmark.form.profile.heavy': 'Generation Heavy',
'benchmark.form.profile.custom': 'Custom',
'benchmark.table.inputTokenLength': 'Prompt Token Length',
'benchmark.table.inputTokenLength': 'Input Token Length',
'benchmark.table.outputTokenLength': 'Output Token Length',
'benchmark.detail.summary.title': 'Summary',
'benchmark.detail.configure.title': 'Configure',
+1 -1
View File
@@ -21,7 +21,7 @@ export default {
'benchmark.form.profile.longContext': 'Long Context',
'benchmark.form.profile.heavy': 'Generation Heavy',
'benchmark.form.profile.custom': 'Custom',
'benchmark.table.inputTokenLength': 'Prompt Token Length',
'benchmark.table.inputTokenLength': 'Input Token Length',
'benchmark.table.outputTokenLength': 'Output Token Length',
'benchmark.detail.summary.title': 'Summary',
'benchmark.detail.configure.title': 'Configure',
+1 -1
View File
@@ -21,7 +21,7 @@ export default {
'benchmark.form.profile.longContext': '长上下文',
'benchmark.form.profile.heavy': '高生成量',
'benchmark.form.profile.custom': '自定义',
'benchmark.table.inputTokenLength': '提示词 Token 长度',
'benchmark.table.inputTokenLength': '输入 Token 长度',
'benchmark.table.outputTokenLength': '输出 Token 长度',
'benchmark.detail.summary.title': '摘要',
'benchmark.detail.configure.title': '配置',
+172
View File
@@ -0,0 +1,172 @@
import OverlayScroller from '@/components/overlay-scroller';
import { SettingOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Button, Checkbox, Col, Popover, Row, Tooltip } from 'antd';
import React from 'react';
import styled from 'styled-components';
const Container = styled.div`
padding: 8px 12px;
padding-right: 4px;
.title {
font-weight: 500;
margin-bottom: 12px;
}
.btn-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 12px;
}
.buttons {
display: flex;
gap: 8px;
justify-content: flex-end;
}
`;
const Title = styled.div`
font-weight: 500;
margin-bottom: 8px;
margin-top: 4px;
`;
const ColumnSettings: React.FC<{
contentHeight: number;
columns: {
title: string;
dataIndex?: string;
children?: { title: string; dataIndex?: string }[];
}[];
selectedColumns?: string[];
grouped?: boolean;
onChange?: (selectedColumns: string[]) => void;
}> = (props) => {
const intl = useIntl();
const { contentHeight, columns, selectedColumns, grouped, onChange } = props;
const [open, setOpen] = React.useState(false);
const handleToggle = () => {
setOpen(!open);
};
const handleSelectAll = () => {
if (grouped) {
const allCols: string[] = [];
columns.forEach((group) => {
group.children?.forEach((col) => {
if (col.dataIndex) {
allCols.push(col.dataIndex);
}
});
});
onChange?.(allCols);
} else {
const allCols = columns
.map((col) => col.dataIndex)
.filter((dataIndex): dataIndex is string => Boolean(dataIndex));
onChange?.(allCols);
}
};
const handleConfirm = () => {
setOpen(false);
};
const contentRender = () => {
return (
<Container>
{!grouped && <div className="title">Column Settings</div>}
<OverlayScroller
maxHeight={contentHeight}
styles={{
wrapper: {
paddingInlineStart: 0
}
}}
>
<Checkbox.Group
value={selectedColumns}
onChange={(checkedValues) => {
onChange?.(checkedValues as string[]);
}}
>
<>
{grouped ? (
columns.map((row, index) => (
<div key={index}>
<Title>{row.title}</Title>
<Row>
{row.children?.map((col) => (
<Col key={col.dataIndex} span={12}>
<Checkbox
value={col.dataIndex}
style={{ marginBottom: 8 }}
>
<span className="text-secondary">{col.title}</span>
</Checkbox>
</Col>
))}
</Row>
</div>
))
) : (
<Row>
{columns.map((col) => (
<Col key={col.dataIndex} span={12}>
<Checkbox
value={col.dataIndex}
style={{ marginBottom: 8 }}
>
<span className="text-secondary">{col.title}</span>
</Checkbox>
</Col>
))}
</Row>
)}
</>
</Checkbox.Group>
</OverlayScroller>
<div className="btn-wrapper">
<Button
size="middle"
onClick={() => {
onChange?.([]);
}}
>
Reset
</Button>
<div className="buttons">
<Button size="middle" type="primary" onClick={handleSelectAll}>
Select All
</Button>
<Button size="middle" type="primary" onClick={handleConfirm}>
Confirm
</Button>
</div>
</div>
</Container>
);
};
return (
<Popover
trigger={'click'}
arrow={false}
placement="bottomRight"
content={contentRender()}
styles={{
root: {
width: '420px'
}
}}
>
<Tooltip title="Column Settings">
<Button onClick={handleToggle} icon={<SettingOutlined />}></Button>
</Tooltip>
</Popover>
);
};
export default ColumnSettings;
@@ -62,7 +62,7 @@ const RightActions: React.FC<RightActionsProps> = ({
icon={<SettingOutlined />}
></Button>
</Tooltip> */}
{settingButton}
{/* {settingButton} */}
<Button
icon={<PlusOutlined></PlusOutlined>}
type="primary"
@@ -140,17 +140,17 @@ const Summary: React.FC = () => {
{
key: '3',
label: 'Total token throughput',
children: `${round(detailData.tokens_per_second_mean || 0, 2)} t/s`
children: `${round(detailData.tokens_per_second_mean || 0, 2)} Tokens/s`
},
{
key: '1',
label: 'Request token throughput',
children: `${round(detailData.prompt_tokens_per_second_mean || 0, 2)} t/s`
children: `${round(detailData.input_tokens_per_second_mean || 0, 2)} Tokens/s`
},
{
key: '2',
label: 'Output token throughput',
children: `${round(detailData.output_tokens_per_second_mean || 0, 2)} t/s`
children: `${round(detailData.output_tokens_per_second_mean || 0, 2)} Tokens/s`
}
];
@@ -19,10 +19,10 @@ const Benchmark: React.FC = () => {
},
{
key: '3',
label: 'Token Length (Prompt/Output)',
label: 'Token Length (Input/Output)',
children: (
<span>
{detailData?.dataset_prompt_tokens || '-'} /{' '}
{detailData?.dataset_input_tokens || '-'} /{' '}
{detailData?.dataset_output_tokens || '-'}
</span>
)
@@ -16,7 +16,7 @@ const Container = styled.div`
const Summary: React.FC = () => {
return (
<Container>
<Section title="Results">
<Section title="Results" minHeight={450}>
<MetricsResult />
<Divider />
<PercentileResult />
@@ -49,9 +49,9 @@ const columns = [
render: (value: number) => round(value, 2)
},
{
title: 'Prompt token throughput ',
dataIndex: 'prompt_tokens_per_second_mean',
path: 'prompt_tokens_per_second_mean',
title: 'Input token throughput ',
dataIndex: 'input_tokens_per_second_mean',
path: 'input_tokens_per_second_mean',
unit: 'Tokens/s',
render: (value: number) => round(value, 2)
},
@@ -36,17 +36,17 @@ const columns = [
render: (value: number) => round(value, 0)
},
{
title: 'Input (t/s)',
title: 'Input (Tokens/s)',
dataIndex: 'prompt_tokens_per_second',
render: (value: number) => round(value, 2)
},
{
title: 'Output (t/s)',
title: 'Output (Tokens/s)',
dataIndex: 'output_tokens_per_second',
render: (value: number) => round(value, 2)
},
{
title: 'Total (t/s)',
title: 'Total (Tokens/s)',
dataIndex: 'tokens_per_second',
render: (value: number) => round(value, 2)
}
@@ -82,10 +82,10 @@ const PercentileResult: React.FC = () => {
size="small"
columns={[
{
title: 'Percentile',
title: <span style={{ fontWeight: 500 }}>Percentile</span>,
dataIndex: 'percentile',
render: (value: string) => (
<span style={{ fontWeight: 400 }}>{value}</span>
<span style={{ fontWeight: 500 }}>{value}</span>
)
},
...columns
+3 -3
View File
@@ -80,14 +80,14 @@ export interface BenchmarkDetail {
time_to_first_token_mean: number;
tokens_per_second_mean: number;
output_tokens_per_second_mean: number;
prompt_tokens_per_second_mean: number;
input_tokens_per_second_mean: number;
name: string;
description: string;
labels: Record<string, any>;
dataset_id: number;
dataset_name: string;
dataset_source: string;
dataset_prompt_tokens: number;
dataset_input_tokens: number;
dataset_output_tokens: number;
cluster_id: number;
model_id: number;
@@ -120,6 +120,6 @@ export interface BenchmarkMetricsFormData {
time_to_first_token_mean: number;
tokens_per_second_mean: number;
output_tokens_per_second_mean: number;
prompt_tokens_per_second_mean: number;
input_tokens_per_second_mean: number;
raw_metrics: Record<string, any>;
}
+21 -3
View File
@@ -3,7 +3,7 @@ import { StatusType } from '@/config/types';
export const BenchmarkStatusValueMap = {
Pending: 'pending',
Claimed: 'claimed',
QUEUED: 'queued',
Running: 'running',
Completed: 'completed',
Error: 'error',
@@ -13,7 +13,7 @@ export const BenchmarkStatusValueMap = {
export const BenchmarkStatusLabelMap = {
[BenchmarkStatusValueMap.Pending]: 'Pending',
[BenchmarkStatusValueMap.Claimed]: 'Claimed',
[BenchmarkStatusValueMap.QUEUED]: 'Queued',
[BenchmarkStatusValueMap.Running]: 'Running',
[BenchmarkStatusValueMap.Completed]: 'Completed',
[BenchmarkStatusValueMap.Error]: 'Error',
@@ -23,7 +23,7 @@ export const BenchmarkStatusLabelMap = {
export const BenchmarkStatus: Record<string, StatusType> = {
[BenchmarkStatusValueMap.Pending]: StatusMaps.transitioning,
[BenchmarkStatusValueMap.Claimed]: StatusMaps.warning,
[BenchmarkStatusValueMap.QUEUED]: StatusMaps.warning,
[BenchmarkStatusValueMap.Running]: StatusMaps.success,
[BenchmarkStatusValueMap.Completed]: StatusMaps.success,
[BenchmarkStatusValueMap.Error]: StatusMaps.error,
@@ -74,3 +74,21 @@ export const profileOptions = [
locale: true
}
];
const DatasetValueMap = {
ShareGPT: 'ShareGPT',
Random: 'Random'
};
export const datasetList = [
{
name: 'ShareGPT',
label: 'ShareGPT',
value: DatasetValueMap.ShareGPT
},
{
name: 'Random',
label: 'Random',
value: DatasetValueMap.Random
}
];
+2 -2
View File
@@ -77,7 +77,7 @@ export interface FormData {
dataset_id: number;
dataset_name: string;
dataset_source: string;
dataset_prompt_tokens: number;
dataset_input_tokens: number;
dataset_output_tokens: number;
total_requests: number;
request_rate: number;
@@ -109,7 +109,7 @@ export interface ProfileOption {
description: string;
dataset_name: string;
dataset_source: string;
dataset_prompt_tokens: number;
dataset_input_tokens: number;
dataset_output_tokens: number;
request_rate: number;
total_requests: number;
-15
View File
@@ -1,4 +1,3 @@
import SealInput from '@/components/seal-form/seal-input';
import SealSelect from '@/components/seal-form/seal-select';
import { PageAction } from '@/config';
import useAppUtils from '@/hooks/use-app-utils';
@@ -31,16 +30,6 @@ const DatasetForm: React.FC = () => {
cancelRequest: cancelProfilesRequest
} = useQueryProfiles();
const handleOnDataSetChange = (value: any, option: any) => {
form.setFieldsValue({
dataset_id: option?.data?.id,
dataset_prompt_tokens: option?.prompt_tokens,
dataset_output_tokens: option?.output_tokens,
request_rate: null,
total_requests: null
});
};
const handleProfileChange = (value: string, option: any) => {
if (value !== ProfileValueMap.Custom) {
const dataset_id = datasetList.find(
@@ -86,13 +75,9 @@ const DatasetForm: React.FC = () => {
></SealSelect>
</Form.Item>
<Form.Item<FormData> hidden name="dataset_id">
<SealInput.Input></SealInput.Input>
</Form.Item>
<RandomSettingsForm
datasetList={datasetList}
datasetLoading={datasetLoading}
handleOnDataSetChange={handleOnDataSetChange}
></RandomSettingsForm>
</>
);
+1 -14
View File
@@ -93,19 +93,6 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
}
}, [showAdvanced, activeKey]);
// const advancedItems = showAdvanced
// ? [
// {
// key: TABKeysMap.ADVANCED,
// label: intl.formatMessage({ id: 'resources.form.advanced' }),
// forceRender: true,
// children: <RandomSettingsForm />
// }
// ]
// : [];
console.log('render form with profile:', showAdvanced);
return (
<ScrollSpyTabs
ref={scrollTabsRef}
@@ -129,7 +116,7 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
form={form}
onFinish={onFinish}
initialValues={{
dataset_prompt_tokens: null,
dataset_input_tokens: null,
dataset_output_tokens: null,
total_requests: null,
request_rate: null,
@@ -101,6 +101,10 @@ const ModelInstanceForm: React.FC = () => {
children: []
}));
if (modelOptions.length === 0) {
return;
}
// preload instances for the first model
const instanceList = await fetchInstanceList({ id: modelOptions[0]?.id });
const instanceOptions = instanceList.map((instance: any) =>
@@ -11,9 +11,8 @@ import { FormData } from '../config/types';
const RandomSettingsForm: React.FC<{
datasetList: Global.BaseOption<number | string>[];
datasetLoading: boolean;
handleOnDataSetChange: (value: any, option: any) => void;
}> = (props) => {
const { datasetList, datasetLoading, handleOnDataSetChange } = props;
const { datasetList, datasetLoading } = props;
const intl = useIntl();
const { action, open } = useFormContext();
const form = Form.useFormInstance();
@@ -45,13 +44,12 @@ const RandomSettingsForm: React.FC<{
value: item.label
}))}
loading={datasetLoading}
onChange={handleOnDataSetChange}
label={intl.formatMessage({ id: 'benchmark.table.dataset' })}
required
></SealSelect>
</Form.Item>
<Form.Item<FormData>
name="dataset_prompt_tokens"
name="dataset_input_tokens"
rules={[
{
required: true,
@@ -99,6 +97,7 @@ const RandomSettingsForm: React.FC<{
</Form.Item>
<Form.Item<FormData>
name="request_rate"
getValueProps={(value) => ({ value: value < 0 ? 'Infinity' : value })}
rules={[
{
required: true,
@@ -107,7 +106,6 @@ const RandomSettingsForm: React.FC<{
]}
>
<SealInputNumber
min={0}
disabled={disabled}
label={intl.formatMessage({ id: 'benchmark.table.requestRate' })}
required
@@ -102,7 +102,7 @@ const useBenchmarkColumns = (
title: (
<span className="flex-column">
<span>{intl.formatMessage({ id: 'benchmark.table.itl' })}</span>
<span>avg (ms)</span>
<span>Avg (ms)</span>
</span>
),
dataIndex: 'inter_token_latency_mean',
@@ -117,7 +117,7 @@ const useBenchmarkColumns = (
title: (
<span className="flex-column">
<span>{intl.formatMessage({ id: 'benchmark.table.tpot' })}</span>
<span>avg (ms)</span>
<span>Avg (ms)</span>
</span>
),
dataIndex: 'time_per_output_token_mean',
@@ -132,7 +132,7 @@ const useBenchmarkColumns = (
title: (
<span className="flex-column">
<span>{intl.formatMessage({ id: 'benchmark.table.ttft' })}</span>
<span>avg (ms)</span>
<span>Avg (ms)</span>
</span>
),
dataIndex: 'time_to_first_token_mean',
+61 -140
View File
@@ -1,43 +1,57 @@
import { SettingOutlined } from '@ant-design/icons';
import ColumnSettings from '@/pages/_components/column-settings';
import { useIntl } from '@umijs/max';
import { Button, Checkbox, Col, Popover, Row, Tooltip } from 'antd';
import React from 'react';
import styled from 'styled-components';
const Container = styled.div`
max-height: 450px;
overflow-y: auto;
padding: 12px;
.title {
font-weight: 500;
margin-bottom: 12px;
}
.btn-wrapper {
margin-top: 12px;
display: flex;
justify-content: space-between;
align-items: center;
border-top: 1px solid var(--ant-color-split);
padding-top: 12px;
}
.buttons {
display: flex;
gap: 8px;
justify-content: flex-end;
}
`;
const useColumnSettings = () => {
const useColumnSettings = (options: { contentHeight: number }) => {
const intl = useIntl();
const [open, setOpen] = React.useState(false);
const { contentHeight } = options;
const [selectedColumns, setSelectedColumns] = React.useState<string[]>([]);
const handleToggle = () => {
setOpen(!open);
const handleOnChange = (columns: string[]) => {
setSelectedColumns(columns);
console.log('selected columns:', columns);
};
const allColumns = [
const resultColumns = [
{
title: intl.formatMessage({ id: 'benchmark.table.rps' }),
dataIndex: 'requests_per_second_mean'
},
{
title: 'Throughput',
dataIndex: 'throughput_mean'
},
{
title: 'Throughput request',
dataIndex: 'throughput_request_mean'
},
{
title: 'Generated Tokens',
dataIndex: 'generated_tokens_mean'
},
{
title: intl.formatMessage({ id: 'benchmark.table.tps' }),
dataIndex: 'tokens_per_second_mean'
},
{
title: intl.formatMessage({ id: 'benchmark.table.itl' }),
dataIndex: 'inter_token_latency_mean'
},
{
title: intl.formatMessage({ id: 'benchmark.table.tpot' }),
dataIndex: 'time_per_output_token_mean'
},
{
title: intl.formatMessage({ id: 'benchmark.table.ttft' }),
dataIndex: 'time_to_first_token_mean'
},
{
title: 'Latency Avg',
dataIndex: 'latency_mean'
}
];
const metadataColumns = [
{
title: intl.formatMessage({ id: 'clusters.title' }),
dataIndex: 'cluster_id'
@@ -58,26 +72,6 @@ const useColumnSettings = () => {
title: intl.formatMessage({ id: 'benchmark.table.dataset' }),
dataIndex: 'dataset_name'
},
{
title: 'Latency',
dataIndex: 'latency_mean'
},
{
title: 'Throughput',
dataIndex: 'throughput_mean'
},
{
title: 'Throughput request',
dataIndex: 'throughput_request_mean'
},
{
title: 'generated tokens',
dataIndex: 'generated_tokens_mean'
},
{
title: 'ITL Avg',
dataIndex: 'inter_token_latency_mean'
},
{
title: intl.formatMessage({ id: 'common.table.status' }),
dataIndex: 'state'
@@ -90,26 +84,6 @@ const useColumnSettings = () => {
title: intl.formatMessage({ id: 'benchmark.table.gpu' }),
dataIndex: 'gpu_summary'
},
{
title: intl.formatMessage({ id: 'benchmark.table.itl' }),
dataIndex: 'inter_token_latency_mean'
},
{
title: intl.formatMessage({ id: 'benchmark.table.tpot' }),
dataIndex: 'time_per_output_token_mean'
},
{
title: intl.formatMessage({ id: 'benchmark.table.ttft' }),
dataIndex: 'time_to_first_token_mean'
},
{
title: intl.formatMessage({ id: 'benchmark.table.rps' }),
dataIndex: 'requests_per_second_mean'
},
{
title: intl.formatMessage({ id: 'benchmark.table.tps' }),
dataIndex: 'tokens_per_second_mean'
},
{
title: intl.formatMessage({ id: 'common.table.createTime' }),
dataIndex: 'created_at'
@@ -120,76 +94,23 @@ const useColumnSettings = () => {
}
];
const contentRender = () => {
return (
<Container>
<div className="title">Column Settings</div>
<Checkbox.Group
value={selectedColumns}
onChange={(checkedValues) => {
setSelectedColumns(checkedValues as string[]);
}}
>
<Row>
{allColumns.map((col) => (
<Col key={col.dataIndex} span={12}>
<Checkbox value={col.dataIndex} style={{ marginBottom: 8 }}>
<span className="text-secondary">{col.title}</span>
</Checkbox>
</Col>
))}
</Row>
</Checkbox.Group>
<div className="btn-wrapper">
<Button
size="middle"
onClick={() => {
setSelectedColumns([]);
}}
>
Clear All
</Button>
<div className="buttons">
<Button
size="middle"
type="primary"
onClick={() => {
setSelectedColumns(allColumns.map((col) => col.dataIndex));
}}
>
Select All
</Button>
<Button
size="middle"
type="primary"
onClick={() => {
setSelectedColumns(allColumns.map((col) => col.dataIndex));
}}
>
Confirm
</Button>
</div>
</div>
</Container>
);
};
const SettingsButton = (
<Popover
trigger={'click'}
arrow={false}
placement="bottomRight"
content={contentRender()}
styles={{
root: {
width: '420px'
<ColumnSettings
contentHeight={contentHeight}
selectedColumns={selectedColumns}
onChange={handleOnChange}
grouped={true}
columns={[
{
title: 'Benchmark Results',
children: resultColumns
},
{
title: 'Metadata',
children: metadataColumns
}
}}
>
<Tooltip title="Column Settings">
<Button onClick={handleToggle} icon={<SettingOutlined />}></Button>
</Tooltip>
</Popover>
]}
></ColumnSettings>
);
return {
+3 -1
View File
@@ -63,7 +63,9 @@ const Benchmark: React.FC = () => {
});
const { openViewLogsModal, closeViewLogsModal, openViewLogsModalStatus } =
useViewLogs();
const { SettingsButton, selectedColumns } = useColumnSettings();
const { SettingsButton, selectedColumns } = useColumnSettings({
contentHeight: 320
});
const { handleStopBenchmark } = useStopBenchmark();
const { datasetList, fetchDatasetData } = useQueryDataset();
@@ -1,6 +1,7 @@
import { useQueryDataList } from '@/hooks/use-query-data-list';
import { useState } from 'react';
import { queryDatasetList } from '../apis';
import { datasetList as datasetOptions } from '../config';
import { DatasetListItem } from '../config/types';
const useQueryDataset = () => {
@@ -17,17 +18,9 @@ const useQueryDataset = () => {
>([]);
const fetchDatasetData = async () => {
const items = await fetchData({
page: -1
});
const list =
items?.map((item) => ({
...item,
label: item.name,
value: item.id
})) || [];
// TODO: may be fetch data from server in the future.
setDatasetList([...list]);
setDatasetList([...datasetOptions]);
};
return {
@@ -41,7 +41,7 @@ export default function useQueryProfiles() {
value: 'Custom',
config: {
dataset_name: '',
dataset_prompt_tokens: null,
dataset_input_tokens: null,
dataset_output_tokens: null,
request_rate: null,
total_requests: null