style: detail layout
This commit is contained in:
@@ -21,7 +21,7 @@ export default {
|
|||||||
'benchmark.form.profile.longContext': 'Long Context',
|
'benchmark.form.profile.longContext': 'Long Context',
|
||||||
'benchmark.form.profile.heavy': 'Generation Heavy',
|
'benchmark.form.profile.heavy': 'Generation Heavy',
|
||||||
'benchmark.form.profile.custom': 'Custom',
|
'benchmark.form.profile.custom': 'Custom',
|
||||||
'benchmark.table.inputTokenLength': 'Input Token Length',
|
'benchmark.table.inputTokenLength': 'Prompt Token Length',
|
||||||
'benchmark.table.outputTokenLength': 'Output Token Length',
|
'benchmark.table.outputTokenLength': 'Output Token Length',
|
||||||
'benchmark.detail.summary.title': 'Summary',
|
'benchmark.detail.summary.title': 'Summary',
|
||||||
'benchmark.detail.configure.title': 'Configure',
|
'benchmark.detail.configure.title': 'Configure',
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export default {
|
|||||||
'benchmark.form.profile.longContext': 'Long Context',
|
'benchmark.form.profile.longContext': 'Long Context',
|
||||||
'benchmark.form.profile.heavy': 'Generation Heavy',
|
'benchmark.form.profile.heavy': 'Generation Heavy',
|
||||||
'benchmark.form.profile.custom': 'Custom',
|
'benchmark.form.profile.custom': 'Custom',
|
||||||
'benchmark.table.inputTokenLength': 'Input Token Length',
|
'benchmark.table.inputTokenLength': 'Prompt Token Length',
|
||||||
'benchmark.table.outputTokenLength': 'Output Token Length',
|
'benchmark.table.outputTokenLength': 'Output Token Length',
|
||||||
'benchmark.detail.summary.title': 'Summary',
|
'benchmark.detail.summary.title': 'Summary',
|
||||||
'benchmark.detail.configure.title': 'Configure',
|
'benchmark.detail.configure.title': 'Configure',
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export default {
|
|||||||
'benchmark.form.profile.longContext': 'Long Context',
|
'benchmark.form.profile.longContext': 'Long Context',
|
||||||
'benchmark.form.profile.heavy': 'Generation Heavy',
|
'benchmark.form.profile.heavy': 'Generation Heavy',
|
||||||
'benchmark.form.profile.custom': 'Custom',
|
'benchmark.form.profile.custom': 'Custom',
|
||||||
'benchmark.table.inputTokenLength': 'Input Token Length',
|
'benchmark.table.inputTokenLength': 'Prompt Token Length',
|
||||||
'benchmark.table.outputTokenLength': 'Output Token Length',
|
'benchmark.table.outputTokenLength': 'Output Token Length',
|
||||||
'benchmark.detail.summary.title': 'Summary',
|
'benchmark.detail.summary.title': 'Summary',
|
||||||
'benchmark.detail.configure.title': 'Configure',
|
'benchmark.detail.configure.title': 'Configure',
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export default {
|
|||||||
'benchmark.form.profile.longContext': '长上下文',
|
'benchmark.form.profile.longContext': '长上下文',
|
||||||
'benchmark.form.profile.heavy': '高生成量',
|
'benchmark.form.profile.heavy': '高生成量',
|
||||||
'benchmark.form.profile.custom': '自定义',
|
'benchmark.form.profile.custom': '自定义',
|
||||||
'benchmark.table.inputTokenLength': '输入 Token 长度',
|
'benchmark.table.inputTokenLength': '提示词 Token 长度',
|
||||||
'benchmark.table.outputTokenLength': '输出 Token 长度',
|
'benchmark.table.outputTokenLength': '输出 Token 长度',
|
||||||
'benchmark.detail.summary.title': '摘要',
|
'benchmark.detail.summary.title': '摘要',
|
||||||
'benchmark.detail.configure.title': '配置',
|
'benchmark.detail.configure.title': '配置',
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
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 const PROFILES_CONFIG_API = '/benchmark-profiles/default-config';
|
||||||
|
export const EXPORT_BENCHMARK_LIST = '/benchmarks/export';
|
||||||
|
|
||||||
export async function queryBenchmarkList(
|
export async function queryBenchmarkList(
|
||||||
params: Global.SearchParams,
|
params: Global.SearchParams,
|
||||||
@@ -103,3 +104,19 @@ export async function queryProfiles(
|
|||||||
cancelToken: options?.token
|
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 useExpandedRowKeys from '@/hooks/use-expanded-row-keys';
|
||||||
import { convertFileSize } from '@/utils';
|
import { convertFileSize } from '@/utils';
|
||||||
import useMemoizedFn from 'ahooks/lib/useMemoizedFn';
|
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 React, { useMemo } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { useDetailContext } from '../../config/detail-context';
|
import { useDetailContext } from '../../config/detail-context';
|
||||||
@@ -32,30 +33,31 @@ const Environment: React.FC = () => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...workerInfo,
|
...workerInfo,
|
||||||
|
..._.pick(gpuData?.[0], ['driver_version', 'runtime_version']),
|
||||||
isMain: true,
|
isMain: true,
|
||||||
children: gpuData
|
children: gpuData
|
||||||
};
|
};
|
||||||
}, [snapshot]);
|
}, [snapshot]);
|
||||||
|
|
||||||
// const subWorkers = useMemo(() => {
|
const subWorkers = useMemo(() => {
|
||||||
// const [[mainWorkerName, mainWorkerInfo]] = Object.entries(snapshot.workers);
|
const [[mainWorkerName, mainWorkerInfo]] = Object.entries(snapshot.workers);
|
||||||
|
|
||||||
// const subOrdinaryWorkers = Object.values(snapshot.instances).filter(
|
const subOrdinaryWorkers = Object.values(snapshot.instances).filter(
|
||||||
// (instance) => instance.worker_name === mainWorkerName
|
(instance) => instance.worker_name === mainWorkerName
|
||||||
// );
|
);
|
||||||
|
|
||||||
// return subOrdinaryWorkers.map((worker) => {
|
return subOrdinaryWorkers.map((worker) => {
|
||||||
// const gpuData = Object.values(snapshot.gpus).filter(
|
const gpuData = Object.values(snapshot.gpus).filter(
|
||||||
// (gpu) => gpu.worker_name === worker.worker_name
|
(gpu) => gpu.worker_name === worker.worker_name
|
||||||
// );
|
);
|
||||||
|
|
||||||
// return {
|
return {
|
||||||
// ...worker,
|
...worker,
|
||||||
// isMain: false,
|
isMain: false,
|
||||||
// children: gpuData
|
children: gpuData
|
||||||
// };
|
};
|
||||||
// });
|
});
|
||||||
// }, [snapshot, mainWorker]);
|
}, [snapshot, mainWorker]);
|
||||||
|
|
||||||
const GPUColumns = [
|
const GPUColumns = [
|
||||||
{
|
{
|
||||||
@@ -68,19 +70,26 @@ const Environment: React.FC = () => {
|
|||||||
<AutoTooltip ghost>{value}</AutoTooltip>
|
<AutoTooltip ghost>{value}</AutoTooltip>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Index',
|
||||||
|
dataIndex: 'index',
|
||||||
|
key: 'index',
|
||||||
|
span: 4,
|
||||||
|
colStyle: { paddingLeft: 48 }
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'Vendor',
|
title: 'Vendor',
|
||||||
dataIndex: 'vendor',
|
dataIndex: 'vendor',
|
||||||
key: 'vendor',
|
key: 'vendor',
|
||||||
span: 4,
|
span: 6,
|
||||||
colStyle: { paddingLeft: 46 }
|
colStyle: { paddingLeft: 110 }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'VRAM',
|
title: 'VRAM',
|
||||||
dataIndex: 'memory_total',
|
dataIndex: 'memory_total',
|
||||||
key: 'memory_total',
|
key: 'memory_total',
|
||||||
label: 'VRAM',
|
label: 'VRAM',
|
||||||
span: 3,
|
span: 4,
|
||||||
render: (value: number, record: any) => convertFileSize(value)
|
render: (value: number, record: any) => convertFileSize(value)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -88,20 +97,8 @@ const Environment: React.FC = () => {
|
|||||||
dataIndex: 'core_total',
|
dataIndex: 'core_total',
|
||||||
key: 'core_total',
|
key: 'core_total',
|
||||||
label: 'Cores',
|
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,
|
span: 4,
|
||||||
colStyle: { paddingLeft: 35 }
|
colStyle: { paddingLeft: 36 }
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -110,31 +107,56 @@ const Environment: React.FC = () => {
|
|||||||
title: 'Worker Name',
|
title: 'Worker Name',
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
key: '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',
|
title: 'System',
|
||||||
dataIndex: 'os',
|
dataIndex: 'os',
|
||||||
key: 'system',
|
key: 'system',
|
||||||
span: 4,
|
span: 5,
|
||||||
render: (os: { name: string; version: string }, record: any) => {
|
render: (os: { name: string; version: string }, record: any) => {
|
||||||
return `${record.os.name}`;
|
return (
|
||||||
|
<AutoTooltip
|
||||||
|
ghost
|
||||||
|
>{`${record.os.name} (${record.os.version})`}</AutoTooltip>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Version',
|
title: 'Runtime Version',
|
||||||
dataIndex: 'version',
|
dataIndex: 'runtime_version',
|
||||||
key: 'version',
|
key: 'runtime_version',
|
||||||
span: 6,
|
span: 3,
|
||||||
render: (val: any, record: any) => {
|
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',
|
title: 'CPU Count',
|
||||||
dataIndex: 'cpu_total',
|
dataIndex: 'cpu_total',
|
||||||
key: 'cpu_total',
|
key: 'cpu_total',
|
||||||
span: 4
|
span: 3
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Memory',
|
title: 'Memory',
|
||||||
@@ -146,8 +168,8 @@ const Environment: React.FC = () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
const dataList = useMemo(() => {
|
const dataList = useMemo(() => {
|
||||||
return [mainWorker];
|
return [mainWorker, ...subWorkers];
|
||||||
}, [mainWorker]);
|
}, [mainWorker, subWorkers]);
|
||||||
|
|
||||||
const handleToggleExpandAll = useMemoizedFn((expanded: boolean) => {
|
const handleToggleExpandAll = useMemoizedFn((expanded: boolean) => {
|
||||||
const keys = dataList?.map((item) => item.id);
|
const keys = dataList?.map((item) => item.id);
|
||||||
@@ -161,6 +183,29 @@ const Environment: React.FC = () => {
|
|||||||
const renderChildren = useMemoizedFn((list: any[]) => {
|
const renderChildren = useMemoizedFn((list: any[]) => {
|
||||||
return (
|
return (
|
||||||
<div style={{ borderRadius: 'var(--ant-table-header-border-radius)' }}>
|
<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>
|
<RowChildren>
|
||||||
{list.map((gpu) => (
|
{list.map((gpu) => (
|
||||||
<Row key={gpu.id} style={{ width: '100%' }} align="middle">
|
<Row key={gpu.id} style={{ width: '100%' }} align="middle">
|
||||||
@@ -174,14 +219,6 @@ const Environment: React.FC = () => {
|
|||||||
flexDirection: 'column'
|
flexDirection: 'column'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
color: 'var(--ant-color-text-tertiary)',
|
|
||||||
fontSize: 12
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{col.title}
|
|
||||||
</span>
|
|
||||||
{col.render
|
{col.render
|
||||||
? col.render((gpu as any)[col.dataIndex], gpu)
|
? col.render((gpu as any)[col.dataIndex], gpu)
|
||||||
: (gpu as any)[col.dataIndex]}
|
: (gpu as any)[col.dataIndex]}
|
||||||
|
|||||||
@@ -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 './section';
|
import Title from './title';
|
||||||
|
|
||||||
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 (Prompt/Out)',
|
label: 'Token Length (Prompt/Output)',
|
||||||
children: (
|
children: (
|
||||||
<span>
|
<span>
|
||||||
{detailData?.dataset_prompt_tokens || '-'} /{' '}
|
{detailData?.dataset_prompt_tokens || '-'} /{' '}
|
||||||
@@ -45,19 +45,19 @@ const Benchmark: React.FC = () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Section title="Benchmark Parameters" minHeight={132}>
|
<div>
|
||||||
|
<Title>Parameters</Title>
|
||||||
<Descriptions
|
<Descriptions
|
||||||
items={items}
|
items={items}
|
||||||
colon={false}
|
colon={false}
|
||||||
column={3}
|
column={3}
|
||||||
layout="vertical"
|
|
||||||
styles={{
|
styles={{
|
||||||
content: {
|
content: {
|
||||||
justifyContent: 'flex-start'
|
justifyContent: 'flex-start'
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
></Descriptions>
|
></Descriptions>
|
||||||
</Section>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
import { Divider } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import BenchMark from './benchmark';
|
import BenchMark from './benchmark';
|
||||||
import Instance from './instance';
|
import Instance from './instance';
|
||||||
import MetricsResult from './metrics-result';
|
import MetricsResult from './metrics-result';
|
||||||
import PercentileResult from './percentile-result';
|
import PercentileResult from './percentile-result';
|
||||||
|
import Section from './section';
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -14,10 +16,16 @@ const Container = styled.div`
|
|||||||
const Summary: React.FC = () => {
|
const Summary: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Instance />
|
<Section title="Results">
|
||||||
<BenchMark />
|
<MetricsResult />
|
||||||
<MetricsResult />
|
<Divider />
|
||||||
<PercentileResult />
|
<PercentileResult />
|
||||||
|
</Section>
|
||||||
|
<Section title="Benchmark Details">
|
||||||
|
<Instance />
|
||||||
|
<Divider />
|
||||||
|
<BenchMark />
|
||||||
|
</Section>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
import AutoTooltip from '@/components/auto-tooltip';
|
||||||
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 './section';
|
import Title from './title';
|
||||||
|
|
||||||
const calcTotalVram = (vram: Record<string, number>) => {
|
const calcTotalVram = (vram: Record<string, number>) => {
|
||||||
return _.sum(_.values(vram));
|
return _.sum(_.values(vram));
|
||||||
@@ -10,6 +11,8 @@ const calcTotalVram = (vram: Record<string, number>) => {
|
|||||||
|
|
||||||
const Instance: React.FC = () => {
|
const Instance: React.FC = () => {
|
||||||
const { detailData } = useDetailContext();
|
const { detailData } = useDetailContext();
|
||||||
|
const [, instanceData] =
|
||||||
|
Object.entries(detailData?.snapshot?.instances || {})[0] || [];
|
||||||
|
|
||||||
const items = useMemo(() => {
|
const items = useMemo(() => {
|
||||||
const { snapshot } = detailData;
|
const { snapshot } = detailData;
|
||||||
@@ -18,25 +21,21 @@ const Instance: React.FC = () => {
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
key: '1',
|
key: '1',
|
||||||
label: 'Instance Name',
|
label: 'Model Name',
|
||||||
children: (
|
children: (
|
||||||
<div className="flex-center gap-8">
|
<AutoTooltip ghost>{detailData?.model_name || '-'}</AutoTooltip>
|
||||||
<span>
|
|
||||||
{detailData?.model_name}/{detailData?.model_instance_name}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: '2',
|
key: '2',
|
||||||
label: 'Worker',
|
label: 'Instance Name',
|
||||||
children: instanceData?.worker_name || '-'
|
children: (
|
||||||
},
|
<AutoTooltip ghost>
|
||||||
{
|
{detailData?.model_instance_name || '-'}
|
||||||
key: '6',
|
</AutoTooltip>
|
||||||
label: 'GPU Type',
|
)
|
||||||
children: instanceData?.gpu_type || '-'
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
key: '5',
|
key: '5',
|
||||||
label: 'Backend',
|
label: 'Backend',
|
||||||
@@ -45,6 +44,13 @@ const Instance: React.FC = () => {
|
|||||||
? `(${instanceData?.backend_version})`
|
? `(${instanceData?.backend_version})`
|
||||||
: ''
|
: ''
|
||||||
}`
|
}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '6',
|
||||||
|
label: 'Model File',
|
||||||
|
children: (
|
||||||
|
<AutoTooltip ghost>{instanceData?.resolved_path || '-'}</AutoTooltip>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}, [detailData]);
|
}, [detailData]);
|
||||||
@@ -144,7 +150,8 @@ const Instance: React.FC = () => {
|
|||||||
}, [detailData]);
|
}, [detailData]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Section title="Model Instance" minHeight={220}>
|
<div>
|
||||||
|
<Title>Instance</Title>
|
||||||
<Descriptions
|
<Descriptions
|
||||||
items={items}
|
items={items}
|
||||||
colon={false}
|
colon={false}
|
||||||
@@ -168,7 +175,7 @@ const Instance: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
></Descriptions>
|
></Descriptions>
|
||||||
</Section>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,46 @@
|
|||||||
import { Descriptions } from 'antd';
|
import { Descriptions } from 'antd';
|
||||||
import _, { round } from 'lodash';
|
import _, { round } from 'lodash';
|
||||||
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 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 = [
|
const columns = [
|
||||||
{
|
// {
|
||||||
title: 'Duration (s)',
|
// title: 'Duration',
|
||||||
dataIndex: 'duration',
|
// dataIndex: 'duration',
|
||||||
path: ['raw_metrics', 'benchmarks', '0', 'duration'],
|
// path: ['raw_metrics', 'benchmarks', '0', 'duration'],
|
||||||
unit: 's',
|
// unit: 's',
|
||||||
render: (value: number) => round(value, 2)
|
// render: (value: number) => round(value, 2)
|
||||||
},
|
// },
|
||||||
|
|
||||||
{
|
// {
|
||||||
title: 'Total Requests',
|
// title: 'Total Requests',
|
||||||
dataIndex: 'total_requests',
|
// dataIndex: 'total_requests',
|
||||||
path: 'total_requests',
|
// path: 'total_requests',
|
||||||
unit: '',
|
// unit: '',
|
||||||
render: (value: number) => round(value, 0)
|
// render: (value: number) => round(value, 0)
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
title: 'Total token throughput',
|
title: 'Total token throughput',
|
||||||
dataIndex: 'tokens_per_second_mean',
|
dataIndex: 'tokens_per_second_mean',
|
||||||
@@ -28,18 +49,25 @@ const columns = [
|
|||||||
render: (value: number) => round(value, 2)
|
render: (value: number) => round(value, 2)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Success Requests',
|
title: 'Prompt token throughput ',
|
||||||
dataIndex: 'successful_requests',
|
dataIndex: 'prompt_tokens_per_second_mean',
|
||||||
path: ['raw_metrics', 'benchmarks', '0'],
|
path: 'prompt_tokens_per_second_mean',
|
||||||
unit: '',
|
unit: 'Tokens/s',
|
||||||
render: (value: number) => {
|
render: (value: number) => round(value, 2)
|
||||||
return (
|
|
||||||
<span style={{ color: 'var(--ant-color-success)' }}>
|
|
||||||
{_.get(value, 'metrics.request_totals.successful')}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
// {
|
||||||
|
// 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',
|
title: 'Output token throughput',
|
||||||
@@ -51,65 +79,103 @@ const columns = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const columnsSub = [
|
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',
|
title: 'Average Request Latency',
|
||||||
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)',
|
|
||||||
dataIndex: 'request_latency_mean',
|
dataIndex: 'request_latency_mean',
|
||||||
path: 'request_latency_mean',
|
path: 'request_latency_mean',
|
||||||
unit: 'ms',
|
unit: 'ms',
|
||||||
render: (value: number) => round(value, 2)
|
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',
|
dataIndex: 'time_to_first_token_mean',
|
||||||
path: 'time_to_first_token_mean',
|
path: 'time_to_first_token_mean',
|
||||||
unit: 'ms',
|
unit: 'ms',
|
||||||
render: (value: number) => round(value, 2)
|
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',
|
dataIndex: 'time_per_output_token_mean',
|
||||||
path: 'time_per_output_token_mean',
|
path: 'time_per_output_token_mean',
|
||||||
unit: 'ms',
|
unit: 'ms',
|
||||||
render: (value: number) => round(value, 2)
|
render: (value: number) => round(value, 2)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '',
|
title: 'Average Inter Token Latency',
|
||||||
dataIndex: '',
|
dataIndex: 'inter_token_latency_mean',
|
||||||
path: '',
|
path: 'inter_token_latency_mean',
|
||||||
unit: '',
|
unit: 'ms',
|
||||||
render: (_: number) => ''
|
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 (
|
return (
|
||||||
<Section title="Metrics Result" minHeight={210}>
|
<div>
|
||||||
{/* <Table
|
<Title>Metrics</Title>
|
||||||
size="small"
|
<Box>
|
||||||
columns={[
|
<Descriptions
|
||||||
{
|
styles={descriptionStyles}
|
||||||
title: 'Metrics',
|
title="Throughput"
|
||||||
dataIndex: 'metrics'
|
items={throughputItems}
|
||||||
},
|
colon={false}
|
||||||
...columns
|
column={1}
|
||||||
]}
|
></Descriptions>
|
||||||
dataSource={buildPercentileTable(metrics)}
|
<Descriptions
|
||||||
rowKey="percentile"
|
styles={descriptionStyles}
|
||||||
pagination={false}
|
title="Latency"
|
||||||
styles={{
|
items={latencyItems}
|
||||||
body: {
|
colon={false}
|
||||||
cell: {
|
column={1}
|
||||||
height: 54
|
></Descriptions>
|
||||||
}
|
<Descriptions
|
||||||
}
|
styles={descriptionStyles}
|
||||||
}}
|
title="Requests"
|
||||||
></Table> */}
|
items={requestItems}
|
||||||
<Descriptions
|
colon={false}
|
||||||
items={items}
|
column={1}
|
||||||
colon={false}
|
></Descriptions>
|
||||||
column={2}
|
</Box>
|
||||||
styles={{
|
</div>
|
||||||
content: {
|
|
||||||
justifyContent: 'flex-end'
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
></Descriptions>
|
|
||||||
</Section>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Table } from 'antd';
|
|||||||
import { round } from 'lodash';
|
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 Title from './title';
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
@@ -76,13 +76,17 @@ const PercentileResult: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Section title="Percentiles Result" minHeight={240}>
|
<div>
|
||||||
|
<Title>Percentile</Title>
|
||||||
<Table
|
<Table
|
||||||
size="small"
|
size="small"
|
||||||
columns={[
|
columns={[
|
||||||
{
|
{
|
||||||
title: 'Percentile',
|
title: 'Percentile',
|
||||||
dataIndex: 'percentile'
|
dataIndex: 'percentile',
|
||||||
|
render: (value: string) => (
|
||||||
|
<span style={{ fontWeight: 500 }}>{value}</span>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
...columns
|
...columns
|
||||||
]}
|
]}
|
||||||
@@ -90,17 +94,15 @@ const PercentileResult: React.FC = () => {
|
|||||||
rowKey="percentile"
|
rowKey="percentile"
|
||||||
pagination={false}
|
pagination={false}
|
||||||
styles={{
|
styles={{
|
||||||
// header: {
|
header: {
|
||||||
// row: {
|
row: {
|
||||||
// background: 'none',
|
backgroundColor: 'transparent'
|
||||||
// borderBottom: '1px solid var(--ant-color-split)'
|
},
|
||||||
// },
|
cell: {
|
||||||
// cell: {
|
fontWeight: 400,
|
||||||
// background: 'none',
|
borderBottom: '1px solid var(--ant-color-split)'
|
||||||
// paddingBottom: 0,
|
}
|
||||||
// borderBottom: '1px solid var(--ant-color-split)'
|
},
|
||||||
// }
|
|
||||||
// },
|
|
||||||
body: {
|
body: {
|
||||||
cell: {
|
cell: {
|
||||||
height: 54
|
height: 54
|
||||||
@@ -108,7 +110,7 @@ const PercentileResult: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
></Table>
|
></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;
|
||||||
@@ -7,6 +7,7 @@ export const BenchmarkStatusValueMap = {
|
|||||||
Running: 'running',
|
Running: 'running',
|
||||||
Completed: 'completed',
|
Completed: 'completed',
|
||||||
Error: 'error',
|
Error: 'error',
|
||||||
|
Stopped: 'stopped',
|
||||||
Unreachable: 'unreachable'
|
Unreachable: 'unreachable'
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@ export const BenchmarkStatusLabelMap = {
|
|||||||
[BenchmarkStatusValueMap.Running]: 'Running',
|
[BenchmarkStatusValueMap.Running]: 'Running',
|
||||||
[BenchmarkStatusValueMap.Completed]: 'Completed',
|
[BenchmarkStatusValueMap.Completed]: 'Completed',
|
||||||
[BenchmarkStatusValueMap.Error]: 'Error',
|
[BenchmarkStatusValueMap.Error]: 'Error',
|
||||||
|
[BenchmarkStatusValueMap.Stopped]: 'Stopped',
|
||||||
[BenchmarkStatusValueMap.Unreachable]: 'Unreachable'
|
[BenchmarkStatusValueMap.Unreachable]: 'Unreachable'
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -25,7 +27,8 @@ export const BenchmarkStatus: Record<string, StatusType> = {
|
|||||||
[BenchmarkStatusValueMap.Running]: StatusMaps.success,
|
[BenchmarkStatusValueMap.Running]: StatusMaps.success,
|
||||||
[BenchmarkStatusValueMap.Completed]: StatusMaps.success,
|
[BenchmarkStatusValueMap.Completed]: StatusMaps.success,
|
||||||
[BenchmarkStatusValueMap.Error]: StatusMaps.error,
|
[BenchmarkStatusValueMap.Error]: StatusMaps.error,
|
||||||
[BenchmarkStatusValueMap.Unreachable]: StatusMaps.error
|
[BenchmarkStatusValueMap.Unreachable]: StatusMaps.error,
|
||||||
|
[BenchmarkStatusValueMap.Stopped]: StatusMaps.warning
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ProfileValueMap = {
|
export const ProfileValueMap = {
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ export interface BenchmarkListItem extends FormData {
|
|||||||
updated_at: string;
|
updated_at: string;
|
||||||
state: string;
|
state: string;
|
||||||
state_message: string;
|
state_message: string;
|
||||||
|
progress: number;
|
||||||
instance_snapshot: InstanceSnapshot;
|
instance_snapshot: InstanceSnapshot;
|
||||||
gpu_snapshot: GPUSnapshot[];
|
gpu_snapshot: GPUSnapshot[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ const BasicForm: React.FC = () => {
|
|||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<SealSelect
|
<SealSelect
|
||||||
|
disabled={action === PageAction.EDIT}
|
||||||
loading={clusterLoading}
|
loading={clusterLoading}
|
||||||
options={clusterList}
|
options={clusterList}
|
||||||
label={intl.formatMessage({ id: 'clusters.title' })}
|
label={intl.formatMessage({ id: 'clusters.title' })}
|
||||||
|
|||||||
@@ -87,13 +87,11 @@ const DatasetForm: React.FC = () => {
|
|||||||
<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>
|
||||||
{profile === 'Custom' && (
|
<RandomSettingsForm
|
||||||
<RandomSettingsForm
|
datasetList={datasetList}
|
||||||
datasetList={datasetList}
|
datasetLoading={datasetLoading}
|
||||||
datasetLoading={datasetLoading}
|
handleOnDataSetChange={handleOnDataSetChange}
|
||||||
handleOnDataSetChange={handleOnDataSetChange}
|
></RandomSettingsForm>
|
||||||
></RandomSettingsForm>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import FormContext from '../config/form-context';
|
|||||||
import { FormData, BenchmarkListItem as ListItem } from '../config/types';
|
import { FormData, BenchmarkListItem as ListItem } from '../config/types';
|
||||||
import Basic from './basic';
|
import Basic from './basic';
|
||||||
import DatasetForm from './dataset';
|
import DatasetForm from './dataset';
|
||||||
import RandomSettingsForm from './random-settings';
|
|
||||||
|
|
||||||
interface ProviderFormProps {
|
interface ProviderFormProps {
|
||||||
ref?: any;
|
ref?: any;
|
||||||
@@ -90,16 +89,16 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
|
|||||||
}
|
}
|
||||||
}, [showAdvanced, activeKey]);
|
}, [showAdvanced, activeKey]);
|
||||||
|
|
||||||
const advancedItems = showAdvanced
|
// const advancedItems = showAdvanced
|
||||||
? [
|
// ? [
|
||||||
{
|
// {
|
||||||
key: TABKeysMap.ADVANCED,
|
// key: TABKeysMap.ADVANCED,
|
||||||
label: intl.formatMessage({ id: 'resources.form.advanced' }),
|
// label: intl.formatMessage({ id: 'resources.form.advanced' }),
|
||||||
forceRender: true,
|
// forceRender: true,
|
||||||
children: <RandomSettingsForm />
|
// children: <RandomSettingsForm />
|
||||||
}
|
// }
|
||||||
]
|
// ]
|
||||||
: [];
|
// : [];
|
||||||
|
|
||||||
console.log('render form with profile:', showAdvanced);
|
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' }),
|
label: intl.formatMessage({ id: 'common.title.config' }),
|
||||||
forceRender: true,
|
forceRender: true,
|
||||||
children: <DatasetForm />
|
children: <DatasetForm />
|
||||||
},
|
}
|
||||||
...advancedItems
|
|
||||||
]}
|
]}
|
||||||
></CollapsePanel>
|
></CollapsePanel>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import AutoTooltip from '@/components/auto-tooltip';
|
|||||||
import StatusTag from '@/components/status-tag';
|
import StatusTag from '@/components/status-tag';
|
||||||
import { tableSorter } from '@/config/settings';
|
import { tableSorter } from '@/config/settings';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Typography } from 'antd';
|
import { Progress, Typography } from 'antd';
|
||||||
import { ColumnsType } from 'antd/es/table';
|
import { ColumnsType } from 'antd/es/table';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
@@ -58,15 +58,24 @@ const useBenchmarkColumns = (
|
|||||||
ellipsis: {
|
ellipsis: {
|
||||||
showTitle: false
|
showTitle: false
|
||||||
},
|
},
|
||||||
width: 100,
|
width: 120,
|
||||||
render: (value: number, record: ListItem) => (
|
render: (value: number, record: ListItem) => (
|
||||||
<StatusTag
|
<span className="flex-center gap-8">
|
||||||
statusValue={{
|
<StatusTag
|
||||||
status: BenchmarkStatus[value],
|
statusValue={{
|
||||||
text: BenchmarkStatusLabelMap[value],
|
status: BenchmarkStatus[value],
|
||||||
message: record.state_message || undefined
|
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>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ 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 useViewLogs from './hooks/use-view-logs';
|
import useViewLogs from './hooks/use-view-logs';
|
||||||
|
import { useExportBenchmark } from './services/use-export-benchmark';
|
||||||
import useQueryDataset from './services/use-query-dataset';
|
import useQueryDataset from './services/use-query-dataset';
|
||||||
|
|
||||||
const Benchmark: React.FC = () => {
|
const Benchmark: React.FC = () => {
|
||||||
@@ -65,6 +65,7 @@ const Benchmark: React.FC = () => {
|
|||||||
const { SettingsButton, selectedColumns } = useColumnSettings();
|
const { SettingsButton, selectedColumns } = useColumnSettings();
|
||||||
|
|
||||||
const { datasetList, fetchDatasetData } = useQueryDataset();
|
const { datasetList, fetchDatasetData } = useQueryDataset();
|
||||||
|
const { exportData } = useExportBenchmark();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchModelList({ page: -1 });
|
fetchModelList({ page: -1 });
|
||||||
@@ -154,13 +155,8 @@ const Benchmark: React.FC = () => {
|
|||||||
handleOnCellClick
|
handleOnCellClick
|
||||||
);
|
);
|
||||||
|
|
||||||
const { exportData } = useExportData({ columns: columns });
|
|
||||||
|
|
||||||
const handleExportData = () => {
|
const handleExportData = () => {
|
||||||
const list = dataSource.dataList.filter((item) =>
|
exportData(rowSelection.selectedRowKeys);
|
||||||
rowSelection.selectedRowKeys.includes(item.id)
|
|
||||||
);
|
|
||||||
exportData(list);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
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 {
|
interface LocalUserFormProps {
|
||||||
handleLogin: (values: any) => void;
|
handleLogin: (values: any) => void;
|
||||||
form: FormInstance;
|
form: FormInstance;
|
||||||
|
loading?: boolean;
|
||||||
loginOption: {
|
loginOption: {
|
||||||
saml: boolean;
|
saml: boolean;
|
||||||
oidc: boolean;
|
oidc: boolean;
|
||||||
@@ -144,6 +145,7 @@ const LocalUserForm: React.FC<LocalUserFormProps> = (props) => {
|
|||||||
htmlType="submit"
|
htmlType="submit"
|
||||||
type="primary"
|
type="primary"
|
||||||
block
|
block
|
||||||
|
loading={props.loading}
|
||||||
style={{ height: '48px', fontSize: '14px' }}
|
style={{ height: '48px', fontSize: '14px' }}
|
||||||
>
|
>
|
||||||
{intl.formatMessage({ id: 'common.button.login' })}
|
{intl.formatMessage({ id: 'common.button.login' })}
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ const LoginForm = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// local user authentication
|
// local user authentication
|
||||||
const { handleLogin } = useLocalAuth({
|
const { handleLogin, submitLoading } = useLocalAuth({
|
||||||
fetchUserInfo,
|
fetchUserInfo,
|
||||||
form,
|
form,
|
||||||
onSuccess: async (userInfo) => {
|
onSuccess: async (userInfo) => {
|
||||||
@@ -254,6 +254,7 @@ const LoginForm = () => {
|
|||||||
<LocalUserForm
|
<LocalUserForm
|
||||||
handleLogin={handleLogin}
|
handleLogin={handleLogin}
|
||||||
form={form}
|
form={form}
|
||||||
|
loading={submitLoading}
|
||||||
loginOption={SSOAuth.options}
|
loginOption={SSOAuth.options}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
import { FormInstance } from 'antd';
|
import { FormInstance } from 'antd';
|
||||||
import CryptoJS from 'crypto-js';
|
import CryptoJS from 'crypto-js';
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
import { useEffect } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { login } from '../apis';
|
import { login } from '../apis';
|
||||||
|
|
||||||
interface UseLocalAuthOptions {
|
interface UseLocalAuthOptions {
|
||||||
@@ -27,6 +27,7 @@ export const useLocalAuth = ({
|
|||||||
form
|
form
|
||||||
}: UseLocalAuthOptions) => {
|
}: UseLocalAuthOptions) => {
|
||||||
const [initialPassword, setInitialPassword] = useAtom(initialPasswordAtom);
|
const [initialPassword, setInitialPassword] = useAtom(initialPasswordAtom);
|
||||||
|
const [submitLoading, setSubmitLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
// Encrypt password before storing
|
// Encrypt password before storing
|
||||||
const encryptPassword = (password: string) => {
|
const encryptPassword = (password: string) => {
|
||||||
@@ -66,6 +67,7 @@ export const useLocalAuth = ({
|
|||||||
|
|
||||||
// click login button
|
// click login button
|
||||||
const handleLogin = async (values: any) => {
|
const handleLogin = async (values: any) => {
|
||||||
|
setSubmitLoading(true);
|
||||||
try {
|
try {
|
||||||
await login({
|
await login({
|
||||||
username: values.username,
|
username: values.username,
|
||||||
@@ -86,6 +88,8 @@ export const useLocalAuth = ({
|
|||||||
onSuccess?.(userInfo);
|
onSuccess?.(userInfo);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
onError?.(error);
|
onError?.(error);
|
||||||
|
} finally {
|
||||||
|
setSubmitLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -94,6 +98,7 @@ export const useLocalAuth = ({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
handleLogin
|
handleLogin,
|
||||||
|
submitLoading
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user