style: detail layout

This commit is contained in:
jialin
2026-01-30 18:48:55 +08:00
parent 667751dc5f
commit 5747cae87a
23 changed files with 506 additions and 234 deletions
+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': 'Input Token Length',
'benchmark.table.inputTokenLength': 'Prompt 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': 'Input Token Length',
'benchmark.table.inputTokenLength': 'Prompt 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': 'Input Token Length',
'benchmark.table.inputTokenLength': 'Prompt 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': '配置',
+17
View File
@@ -10,6 +10,7 @@ import {
export const BENCHMARKS_API = '/benchmarks';
export const DATASETS_API = '/datasets';
export const PROFILES_CONFIG_API = '/benchmark-profiles/default-config';
export const EXPORT_BENCHMARK_LIST = '/benchmarks/export';
export async function queryBenchmarkList(
params: Global.SearchParams,
@@ -103,3 +104,19 @@ export async function queryProfiles(
cancelToken: options?.token
});
}
export async function exportBenchmarkList(
params: {
ids?: number[];
},
options?: {
token?: CancelToken;
}
) {
return request(`${BENCHMARKS_API}/export`, {
method: 'POST',
data: params.ids,
responseType: 'blob',
cancelToken: options?.token
});
}
@@ -4,7 +4,8 @@ import SealTable from '@/components/seal-table/index';
import useExpandedRowKeys from '@/hooks/use-expanded-row-keys';
import { convertFileSize } from '@/utils';
import useMemoizedFn from 'ahooks/lib/useMemoizedFn';
import { Col, Row } from 'antd';
import { Col, Row, Tag } from 'antd';
import _ from 'lodash';
import React, { useMemo } from 'react';
import styled from 'styled-components';
import { useDetailContext } from '../../config/detail-context';
@@ -32,30 +33,31 @@ const Environment: React.FC = () => {
return {
...workerInfo,
..._.pick(gpuData?.[0], ['driver_version', 'runtime_version']),
isMain: true,
children: gpuData
};
}, [snapshot]);
// const subWorkers = useMemo(() => {
// const [[mainWorkerName, mainWorkerInfo]] = Object.entries(snapshot.workers);
const subWorkers = useMemo(() => {
const [[mainWorkerName, mainWorkerInfo]] = Object.entries(snapshot.workers);
// const subOrdinaryWorkers = Object.values(snapshot.instances).filter(
// (instance) => instance.worker_name === mainWorkerName
// );
const subOrdinaryWorkers = Object.values(snapshot.instances).filter(
(instance) => instance.worker_name === mainWorkerName
);
// return subOrdinaryWorkers.map((worker) => {
// const gpuData = Object.values(snapshot.gpus).filter(
// (gpu) => gpu.worker_name === worker.worker_name
// );
return subOrdinaryWorkers.map((worker) => {
const gpuData = Object.values(snapshot.gpus).filter(
(gpu) => gpu.worker_name === worker.worker_name
);
// return {
// ...worker,
// isMain: false,
// children: gpuData
// };
// });
// }, [snapshot, mainWorker]);
return {
...worker,
isMain: false,
children: gpuData
};
});
}, [snapshot, mainWorker]);
const GPUColumns = [
{
@@ -68,19 +70,26 @@ const Environment: React.FC = () => {
<AutoTooltip ghost>{value}</AutoTooltip>
)
},
{
title: 'Index',
dataIndex: 'index',
key: 'index',
span: 4,
colStyle: { paddingLeft: 48 }
},
{
title: 'Vendor',
dataIndex: 'vendor',
key: 'vendor',
span: 4,
colStyle: { paddingLeft: 46 }
span: 6,
colStyle: { paddingLeft: 110 }
},
{
title: 'VRAM',
dataIndex: 'memory_total',
key: 'memory_total',
label: 'VRAM',
span: 3,
span: 4,
render: (value: number, record: any) => convertFileSize(value)
},
{
@@ -88,20 +97,8 @@ const Environment: React.FC = () => {
dataIndex: 'core_total',
key: 'core_total',
label: 'Cores',
span: 3
},
{
title: 'Runtime Version',
dataIndex: 'runtime_version',
key: 'runtime_version',
span: 4
},
{
title: 'Driver Version',
dataIndex: 'driver_version',
key: 'driver_version',
span: 4,
colStyle: { paddingLeft: 35 }
colStyle: { paddingLeft: 36 }
}
];
@@ -110,31 +107,56 @@ const Environment: React.FC = () => {
title: 'Worker Name',
dataIndex: 'name',
key: 'name',
span: 6
span: 6,
render: (value: string, record: any) => {
return (
<>
<AutoTooltip ghost>{value}</AutoTooltip>
{record.isMain && (
<Tag color="geekblue" style={{ marginLeft: 8 }}>
Main
</Tag>
)}
</>
);
}
},
{
title: 'System',
dataIndex: 'os',
key: 'system',
span: 4,
span: 5,
render: (os: { name: string; version: string }, record: any) => {
return `${record.os.name}`;
return (
<AutoTooltip
ghost
>{`${record.os.name} (${record.os.version})`}</AutoTooltip>
);
}
},
{
title: 'Version',
dataIndex: 'version',
key: 'version',
span: 6,
title: 'Runtime Version',
dataIndex: 'runtime_version',
key: 'runtime_version',
span: 3,
render: (val: any, record: any) => {
return <AutoTooltip ghost>{record.os.version}</AutoTooltip>;
return <AutoTooltip ghost>{record.runtime_version}</AutoTooltip>;
}
},
{
title: 'Driver Version',
dataIndex: 'driver_version',
key: 'driver_version',
span: 3,
render: (val: any, record: any) => {
return <AutoTooltip ghost>{record.driver_version}</AutoTooltip>;
}
},
{
title: 'CPU Count',
dataIndex: 'cpu_total',
key: 'cpu_total',
span: 4
span: 3
},
{
title: 'Memory',
@@ -146,8 +168,8 @@ const Environment: React.FC = () => {
];
const dataList = useMemo(() => {
return [mainWorker];
}, [mainWorker]);
return [mainWorker, ...subWorkers];
}, [mainWorker, subWorkers]);
const handleToggleExpandAll = useMemoizedFn((expanded: boolean) => {
const keys = dataList?.map((item) => item.id);
@@ -161,6 +183,29 @@ const Environment: React.FC = () => {
const renderChildren = useMemoizedFn((list: any[]) => {
return (
<div style={{ borderRadius: 'var(--ant-table-header-border-radius)' }}>
<Row style={{ width: '100%' }} align="middle">
{GPUColumns.map((col) => (
<Col key={col.key} span={col.span} style={{ ...col.colStyle }}>
<span
style={{
display: 'flex',
alignItems: 'flex-start',
gap: 4,
flexDirection: 'column'
}}
>
<span
style={{
color: 'var(--ant-color-text-tertiary)',
fontSize: 12
}}
>
{col.title}
</span>
</span>
</Col>
))}
</Row>
<RowChildren>
{list.map((gpu) => (
<Row key={gpu.id} style={{ width: '100%' }} align="middle">
@@ -174,14 +219,6 @@ const Environment: React.FC = () => {
flexDirection: 'column'
}}
>
<span
style={{
color: 'var(--ant-color-text-tertiary)',
fontSize: 12
}}
>
{col.title}
</span>
{col.render
? col.render((gpu as any)[col.dataIndex], gpu)
: (gpu as any)[col.dataIndex]}
@@ -1,7 +1,7 @@
import { Descriptions, DescriptionsProps } from 'antd';
import React from 'react';
import { useDetailContext } from '../../config/detail-context';
import Section from './section';
import Title from './title';
const Benchmark: React.FC = () => {
const { detailData } = useDetailContext();
@@ -19,7 +19,7 @@ const Benchmark: React.FC = () => {
},
{
key: '3',
label: 'Token Length (Prompt/Out)',
label: 'Token Length (Prompt/Output)',
children: (
<span>
{detailData?.dataset_prompt_tokens || '-'} /{' '}
@@ -45,19 +45,19 @@ const Benchmark: React.FC = () => {
];
return (
<Section title="Benchmark Parameters" minHeight={132}>
<div>
<Title>Parameters</Title>
<Descriptions
items={items}
colon={false}
column={3}
layout="vertical"
styles={{
content: {
justifyContent: 'flex-start'
}
}}
></Descriptions>
</Section>
</div>
);
};
@@ -1,9 +1,11 @@
import { Divider } from 'antd';
import React from 'react';
import styled from 'styled-components';
import BenchMark from './benchmark';
import Instance from './instance';
import MetricsResult from './metrics-result';
import PercentileResult from './percentile-result';
import Section from './section';
const Container = styled.div`
display: flex;
@@ -14,10 +16,16 @@ const Container = styled.div`
const Summary: React.FC = () => {
return (
<Container>
<Instance />
<BenchMark />
<MetricsResult />
<PercentileResult />
<Section title="Results">
<MetricsResult />
<Divider />
<PercentileResult />
</Section>
<Section title="Benchmark Details">
<Instance />
<Divider />
<BenchMark />
</Section>
</Container>
);
};
@@ -1,8 +1,9 @@
import AutoTooltip from '@/components/auto-tooltip';
import { Descriptions, Flex, Tag } from 'antd';
import _ from 'lodash';
import React, { useMemo } from 'react';
import { useDetailContext } from '../../config/detail-context';
import Section from './section';
import Title from './title';
const calcTotalVram = (vram: Record<string, number>) => {
return _.sum(_.values(vram));
@@ -10,6 +11,8 @@ const calcTotalVram = (vram: Record<string, number>) => {
const Instance: React.FC = () => {
const { detailData } = useDetailContext();
const [, instanceData] =
Object.entries(detailData?.snapshot?.instances || {})[0] || [];
const items = useMemo(() => {
const { snapshot } = detailData;
@@ -18,25 +21,21 @@ const Instance: React.FC = () => {
return [
{
key: '1',
label: 'Instance Name',
label: 'Model Name',
children: (
<div className="flex-center gap-8">
<span>
{detailData?.model_name}/{detailData?.model_instance_name}
</span>
</div>
<AutoTooltip ghost>{detailData?.model_name || '-'}</AutoTooltip>
)
},
{
key: '2',
label: 'Worker',
children: instanceData?.worker_name || '-'
},
{
key: '6',
label: 'GPU Type',
children: instanceData?.gpu_type || '-'
label: 'Instance Name',
children: (
<AutoTooltip ghost>
{detailData?.model_instance_name || '-'}
</AutoTooltip>
)
},
{
key: '5',
label: 'Backend',
@@ -45,6 +44,13 @@ const Instance: React.FC = () => {
? `(${instanceData?.backend_version})`
: ''
}`
},
{
key: '6',
label: 'Model File',
children: (
<AutoTooltip ghost>{instanceData?.resolved_path || '-'}</AutoTooltip>
)
}
];
}, [detailData]);
@@ -144,7 +150,8 @@ const Instance: React.FC = () => {
}, [detailData]);
return (
<Section title="Model Instance" minHeight={220}>
<div>
<Title>Instance</Title>
<Descriptions
items={items}
colon={false}
@@ -168,7 +175,7 @@ const Instance: React.FC = () => {
}
}}
></Descriptions>
</Section>
</div>
);
};
@@ -1,25 +1,46 @@
import { Descriptions } from 'antd';
import _, { round } from 'lodash';
import React from 'react';
import styled from 'styled-components';
import { useDetailContext } from '../../config/detail-context';
import Section from './section';
import Title from './title';
const Card = styled.div`
height: 78px;
padding: 12px 16px;
border: 1px solid var(--ant-color-border);
border-radius: var(--ant-border-radius);
background-color: var(--ant-color-bg-container);
`;
const Box = styled.div`
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 16px;
`;
const DescWrapper = styled.div`
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 24px;
`;
const columns = [
{
title: 'Duration (s)',
dataIndex: 'duration',
path: ['raw_metrics', 'benchmarks', '0', 'duration'],
unit: 's',
render: (value: number) => round(value, 2)
},
// {
// title: 'Duration',
// dataIndex: 'duration',
// path: ['raw_metrics', 'benchmarks', '0', 'duration'],
// unit: 's',
// render: (value: number) => round(value, 2)
// },
{
title: 'Total Requests',
dataIndex: 'total_requests',
path: 'total_requests',
unit: '',
render: (value: number) => round(value, 0)
},
// {
// title: 'Total Requests',
// dataIndex: 'total_requests',
// path: 'total_requests',
// unit: '',
// render: (value: number) => round(value, 0)
// },
{
title: 'Total token throughput',
dataIndex: 'tokens_per_second_mean',
@@ -28,18 +49,25 @@ const columns = [
render: (value: number) => round(value, 2)
},
{
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: 'Prompt token throughput ',
dataIndex: 'prompt_tokens_per_second_mean',
path: 'prompt_tokens_per_second_mean',
unit: 'Tokens/s',
render: (value: number) => round(value, 2)
},
// {
// 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: 'Output token throughput',
@@ -51,65 +79,103 @@ const columns = [
];
const columnsSub = [
// {
// 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: 'Concurrency',
// dataIndex: 'request_concurrency',
// path: ['raw_metrics', 'benchmarks', '0'],
// unit: '',
// render: (value: number) => {
// return round(
// _.get(value, 'metrics.request_concurrency.successful.mean'),
// 0
// );
// }
// },
{
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: 'Concurrency',
dataIndex: 'request_concurrency',
path: ['raw_metrics', 'benchmarks', '0'],
unit: '',
render: (value: number) => {
return round(
_.get(value, 'metrics.request_concurrency.successful.mean'),
0
);
}
},
{
title: 'Request token throughput ',
dataIndex: 'prompt_tokens_per_second_mean',
path: 'prompt_tokens_per_second_mean',
unit: 'Tokens/s',
render: (value: number) => round(value, 2)
},
{
title: 'Average Request Latency (ms)',
title: 'Average Request Latency',
dataIndex: 'request_latency_mean',
path: 'request_latency_mean',
unit: 'ms',
render: (value: number) => round(value, 2)
},
{
title: 'Average Time To First Token (ms)',
title: 'Average Time To First Token',
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)',
title: 'Average Time Per Output Token',
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) => ''
title: 'Average Inter Token Latency',
dataIndex: 'inter_token_latency_mean',
path: 'inter_token_latency_mean',
unit: 'ms',
render: (value: number) => round(value, 2)
}
];
const requestFields = [
{
label: 'Total Requests',
key: 'total_requests',
dataIndex: 'total_requests',
path: 'total_requests',
precision: 0,
render: (value: number) => round(value, 0),
unit: ''
},
{
label: 'Success Requests',
key: 'total_requests',
dataIndex: 'successful_requests',
path: ['raw_metrics', 'benchmarks', '0'],
render: (value: number) =>
round(_.get(value, ['metrics', 'request_totals', 'successful']), 0),
precision: 0,
color: 'var(--ant-color-success)',
unit: ''
},
{
label: 'Failed Requests',
key: 'total_requests',
dataIndex: 'failed_requests',
path: ['raw_metrics', 'benchmarks', '0'],
render: (value: number) =>
round(_.get(value, ['metrics', 'request_totals', 'errored']), 0),
precision: 0,
color: 'var(--ant-color-error)',
unit: ''
},
{
label: 'Concurrency',
key: 'request_concurrency',
dataIndex: 'request_concurrency',
path: ['raw_metrics', 'benchmarks', '0'],
render: (value: number) =>
round(_.get(value, 'metrics.request_concurrency.successful.mean'), 0),
precision: 0,
unit: ''
}
];
@@ -148,39 +214,94 @@ const PercentileResult: React.FC = () => {
})
);
const throughputItems = columns.map(
({ title, dataIndex, path, render, unit }) => ({
key: dataIndex,
label: title,
children: unit ? (
<span className="flex-center">
{render(_.get(detailData, path) ?? 0)}{' '}
<span className="m-l-4">({unit})</span>
</span>
) : (
render(_.get(detailData, path) ?? 0)
)
})
);
const latencyItems = columnsSub.map(
({ title, dataIndex, path, render, unit }) => ({
key: dataIndex,
label: title,
children: unit ? (
<span className="flex-center">
{render(_.get(detailData, path) ?? 0)}{' '}
<span className="m-l-4">({unit})</span>
</span>
) : (
render(_.get(detailData, path) ?? 0)
)
})
);
const requestItems = requestFields.map(
({ label, dataIndex, path, render, unit, color }) => ({
key: dataIndex,
label: label,
children: unit ? (
<span className="flex-center" style={{ color: color }}>
{render(_.get(detailData, path) ?? 0)}{' '}
<span className="m-l-4">({unit})</span>
</span>
) : (
<span style={{ color: color }}>
{render(_.get(detailData, path) ?? 0)}
</span>
)
})
);
const descriptionStyles = {
header: {
marginBottom: 8
},
title: {
fontWeight: 500,
color: 'var(--ant-color-text-secondary)',
fontSize: 14
},
content: {
justifyContent: 'flex-start'
}
};
return (
<Section title="Metrics Result" minHeight={210}>
{/* <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>
<div>
<Title>Metrics</Title>
<Box>
<Descriptions
styles={descriptionStyles}
title="Throughput"
items={throughputItems}
colon={false}
column={1}
></Descriptions>
<Descriptions
styles={descriptionStyles}
title="Latency"
items={latencyItems}
colon={false}
column={1}
></Descriptions>
<Descriptions
styles={descriptionStyles}
title="Requests"
items={requestItems}
colon={false}
column={1}
></Descriptions>
</Box>
</div>
);
};
@@ -2,7 +2,7 @@ import { Table } from 'antd';
import { round } from 'lodash';
import React from 'react';
import { useDetailContext } from '../../config/detail-context';
import Section from './section';
import Title from './title';
const columns = [
{
@@ -76,13 +76,17 @@ const PercentileResult: React.FC = () => {
};
return (
<Section title="Percentiles Result" minHeight={240}>
<div>
<Title>Percentile</Title>
<Table
size="small"
columns={[
{
title: 'Percentile',
dataIndex: 'percentile'
dataIndex: 'percentile',
render: (value: string) => (
<span style={{ fontWeight: 500 }}>{value}</span>
)
},
...columns
]}
@@ -90,17 +94,15 @@ const PercentileResult: React.FC = () => {
rowKey="percentile"
pagination={false}
styles={{
// header: {
// row: {
// background: 'none',
// borderBottom: '1px solid var(--ant-color-split)'
// },
// cell: {
// background: 'none',
// paddingBottom: 0,
// borderBottom: '1px solid var(--ant-color-split)'
// }
// },
header: {
row: {
backgroundColor: 'transparent'
},
cell: {
fontWeight: 400,
borderBottom: '1px solid var(--ant-color-split)'
}
},
body: {
cell: {
height: 54
@@ -108,7 +110,7 @@ const PercentileResult: React.FC = () => {
}
}}
></Table>
</Section>
</div>
);
};
@@ -0,0 +1,21 @@
import { BulbOutlined } from '@ant-design/icons';
import styled from 'styled-components';
const Content = styled.div`
font-weight: 500;
font-size: 14px;
margin-bottom: 16px;
`;
const Title: React.FC<{ children: React.ReactNode }> = ({ children }) => {
return (
<Content>
<BulbOutlined
style={{ marginRight: 8, color: 'var(--ant-color-text-tertiary)' }}
/>
{children}
</Content>
);
};
export default Title;
+4 -1
View File
@@ -7,6 +7,7 @@ export const BenchmarkStatusValueMap = {
Running: 'running',
Completed: 'completed',
Error: 'error',
Stopped: 'stopped',
Unreachable: 'unreachable'
};
@@ -16,6 +17,7 @@ export const BenchmarkStatusLabelMap = {
[BenchmarkStatusValueMap.Running]: 'Running',
[BenchmarkStatusValueMap.Completed]: 'Completed',
[BenchmarkStatusValueMap.Error]: 'Error',
[BenchmarkStatusValueMap.Stopped]: 'Stopped',
[BenchmarkStatusValueMap.Unreachable]: 'Unreachable'
};
@@ -25,7 +27,8 @@ export const BenchmarkStatus: Record<string, StatusType> = {
[BenchmarkStatusValueMap.Running]: StatusMaps.success,
[BenchmarkStatusValueMap.Completed]: StatusMaps.success,
[BenchmarkStatusValueMap.Error]: StatusMaps.error,
[BenchmarkStatusValueMap.Unreachable]: StatusMaps.error
[BenchmarkStatusValueMap.Unreachable]: StatusMaps.error,
[BenchmarkStatusValueMap.Stopped]: StatusMaps.warning
};
export const ProfileValueMap = {
+1
View File
@@ -91,6 +91,7 @@ export interface BenchmarkListItem extends FormData {
updated_at: string;
state: string;
state_message: string;
progress: number;
instance_snapshot: InstanceSnapshot;
gpu_snapshot: GPUSnapshot[];
}
+1
View File
@@ -77,6 +77,7 @@ const BasicForm: React.FC = () => {
]}
>
<SealSelect
disabled={action === PageAction.EDIT}
loading={clusterLoading}
options={clusterList}
label={intl.formatMessage({ id: 'clusters.title' })}
+5 -7
View File
@@ -87,13 +87,11 @@ const DatasetForm: React.FC = () => {
<Form.Item<FormData> hidden name="dataset_id">
<SealInput.Input></SealInput.Input>
</Form.Item>
{profile === 'Custom' && (
<RandomSettingsForm
datasetList={datasetList}
datasetLoading={datasetLoading}
handleOnDataSetChange={handleOnDataSetChange}
></RandomSettingsForm>
)}
<RandomSettingsForm
datasetList={datasetList}
datasetLoading={datasetLoading}
handleOnDataSetChange={handleOnDataSetChange}
></RandomSettingsForm>
</>
);
};
+11 -13
View File
@@ -17,7 +17,6 @@ import FormContext from '../config/form-context';
import { FormData, BenchmarkListItem as ListItem } from '../config/types';
import Basic from './basic';
import DatasetForm from './dataset';
import RandomSettingsForm from './random-settings';
interface ProviderFormProps {
ref?: any;
@@ -90,16 +89,16 @@ 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 />
}
]
: [];
// const advancedItems = showAdvanced
// ? [
// {
// key: TABKeysMap.ADVANCED,
// label: intl.formatMessage({ id: 'resources.form.advanced' }),
// forceRender: true,
// children: <RandomSettingsForm />
// }
// ]
// : [];
console.log('render form with profile:', showAdvanced);
@@ -144,8 +143,7 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
label: intl.formatMessage({ id: 'common.title.config' }),
forceRender: true,
children: <DatasetForm />
},
...advancedItems
}
]}
></CollapsePanel>
</Form>
@@ -3,7 +3,7 @@ import AutoTooltip from '@/components/auto-tooltip';
import StatusTag from '@/components/status-tag';
import { tableSorter } from '@/config/settings';
import { useIntl } from '@umijs/max';
import { Typography } from 'antd';
import { Progress, Typography } from 'antd';
import { ColumnsType } from 'antd/es/table';
import _ from 'lodash';
import { useMemo } from 'react';
@@ -58,15 +58,24 @@ const useBenchmarkColumns = (
ellipsis: {
showTitle: false
},
width: 100,
width: 120,
render: (value: number, record: ListItem) => (
<StatusTag
statusValue={{
status: BenchmarkStatus[value],
text: BenchmarkStatusLabelMap[value],
message: record.state_message || undefined
}}
/>
<span className="flex-center gap-8">
<StatusTag
statusValue={{
status: BenchmarkStatus[value],
text: BenchmarkStatusLabelMap[value],
message: record.state_message || undefined
}}
/>
{record.progress !== undefined && record.progress < 100 && (
<Progress
type="circle"
percent={_.round(record.progress, 0)}
size={20}
/>
)}
</span>
)
},
// {
+3 -7
View File
@@ -27,8 +27,8 @@ import { FormData, BenchmarkListItem as ListItem } from './config/types';
import useBenchmarkColumns from './hooks/use-benchmark-columns';
import useColumnSettings from './hooks/use-column-settings';
import useCreateBenchmark from './hooks/use-create-benchmark';
import useExportData from './hooks/use-export-data';
import useViewLogs from './hooks/use-view-logs';
import { useExportBenchmark } from './services/use-export-benchmark';
import useQueryDataset from './services/use-query-dataset';
const Benchmark: React.FC = () => {
@@ -65,6 +65,7 @@ const Benchmark: React.FC = () => {
const { SettingsButton, selectedColumns } = useColumnSettings();
const { datasetList, fetchDatasetData } = useQueryDataset();
const { exportData } = useExportBenchmark();
useEffect(() => {
fetchModelList({ page: -1 });
@@ -154,13 +155,8 @@ const Benchmark: React.FC = () => {
handleOnCellClick
);
const { exportData } = useExportData({ columns: columns });
const handleExportData = () => {
const list = dataSource.dataList.filter((item) =>
rowSelection.selectedRowKeys.includes(item.id)
);
exportData(list);
exportData(rowSelection.selectedRowKeys);
};
return (
@@ -0,0 +1,45 @@
import { GPUSTACK_API_BASE_URL } from '@/config/settings';
import { downloadFile } from '@/utils/download-stream';
import { message } from 'antd';
import { EXPORT_BENCHMARK_LIST } from '../apis';
const matchFilename = (disposition: string | null): string | undefined => {
if (!disposition) return '';
const match = disposition.match(/filename="?([^"]+)"?/);
const filename = match ? match[1] : '';
return filename;
};
export function useExportBenchmark() {
const exportData = async (data: any[]) => {
try {
const res = await fetch(
`${GPUSTACK_API_BASE_URL}${EXPORT_BENCHMARK_LIST}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}
);
// header
const contentDispostion = res.headers.get('content-Disposition');
const filename =
matchFilename(contentDispostion) || `benchmark-export.yml`;
if (res.ok) {
const blob = await res.blob();
downloadFile(blob, filename);
} else {
message.error('Download failed');
}
} catch (error) {
message.error('Download failed');
}
};
return {
exportData
};
}
@@ -36,6 +36,7 @@ const useStyles = createStyles(({ token, css }) => {
interface LocalUserFormProps {
handleLogin: (values: any) => void;
form: FormInstance;
loading?: boolean;
loginOption: {
saml: boolean;
oidc: boolean;
@@ -144,6 +145,7 @@ const LocalUserForm: React.FC<LocalUserFormProps> = (props) => {
htmlType="submit"
type="primary"
block
loading={props.loading}
style={{ height: '48px', fontSize: '14px' }}
>
{intl.formatMessage({ id: 'common.button.login' })}
+2 -1
View File
@@ -148,7 +148,7 @@ const LoginForm = () => {
};
// local user authentication
const { handleLogin } = useLocalAuth({
const { handleLogin, submitLoading } = useLocalAuth({
fetchUserInfo,
form,
onSuccess: async (userInfo) => {
@@ -254,6 +254,7 @@ const LoginForm = () => {
<LocalUserForm
handleLogin={handleLogin}
form={form}
loading={submitLoading}
loginOption={SSOAuth.options}
/>
)}
+7 -2
View File
@@ -10,7 +10,7 @@ import {
import { FormInstance } from 'antd';
import CryptoJS from 'crypto-js';
import { useAtom } from 'jotai';
import { useEffect } from 'react';
import { useEffect, useState } from 'react';
import { login } from '../apis';
interface UseLocalAuthOptions {
@@ -27,6 +27,7 @@ export const useLocalAuth = ({
form
}: UseLocalAuthOptions) => {
const [initialPassword, setInitialPassword] = useAtom(initialPasswordAtom);
const [submitLoading, setSubmitLoading] = useState<boolean>(false);
// Encrypt password before storing
const encryptPassword = (password: string) => {
@@ -66,6 +67,7 @@ export const useLocalAuth = ({
// click login button
const handleLogin = async (values: any) => {
setSubmitLoading(true);
try {
await login({
username: values.username,
@@ -86,6 +88,8 @@ export const useLocalAuth = ({
onSuccess?.(userInfo);
} catch (error: any) {
onError?.(error);
} finally {
setSubmitLoading(false);
}
};
@@ -94,6 +98,7 @@ export const useLocalAuth = ({
}, []);
return {
handleLogin
handleLogin,
submitLoading
};
};