feat: cluster_count on dashboard

This commit is contained in:
jialin
2025-11-06 19:38:01 +08:00
parent fadff34f5f
commit cd2942405a
9 changed files with 143 additions and 51 deletions
+4 -1
View File
@@ -154,7 +154,10 @@ const LineChart: React.FC<ChartProps> = (props) => {
return ( return (
<> <>
{!seriesData.length ? ( {!seriesData.length ? (
<EmptyData height={height} title={title}></EmptyData> <EmptyData
height={height}
title={_.get(title, 'text', title || '')}
></EmptyData>
) : ( ) : (
<Chart <Chart
height={height} height={height}
+4 -2
View File
@@ -6,8 +6,10 @@ export const DASHBOARD_API = '/dashboard';
export const DASHBOARD_USAGE_API = `${DASHBOARD_API}/usage`; export const DASHBOARD_USAGE_API = `${DASHBOARD_API}/usage`;
export const DASHBOARD_STATS_API = `${DASHBOARD_API}/usage/stats`; export const DASHBOARD_STATS_API = `${DASHBOARD_API}/usage/stats`;
export async function queryDashboardData() { export async function queryDashboardData(params?: { cluster_id?: number }) {
return request(DASHBOARD_API); return request(DASHBOARD_API, {
params
});
} }
export async function queryDashboardUsageData<T>( export async function queryDashboardUsageData<T>(
@@ -1,39 +1,17 @@
import { memo, useCallback, useEffect, useState } from 'react';
import { queryDashboardData } from '../apis';
import DashboardContext from '../config/dashboard-context';
import { DashboardProps } from '../config/types';
import ActiveTable from './active-table'; import ActiveTable from './active-table';
import Overview from './over-view'; import Overview from './over-view';
import SystemLoad from './system-load'; import SystemLoad from './system-load';
import Usage from './usage'; import Usage from './usage';
const Dashboard: React.FC<{ setLoading: (loading: boolean) => void }> = ({ const Dashboard: React.FC = () => {
setLoading
}) => {
const [data, setData] = useState<DashboardProps>({} as DashboardProps);
const getDashboardData = useCallback(async () => {
try {
setLoading(true);
const res = await queryDashboardData();
setData(res);
setLoading(false);
} catch (error) {
setLoading(false);
setData({} as DashboardProps);
}
}, []);
useEffect(() => {
getDashboardData();
}, []);
return ( return (
<DashboardContext.Provider value={{ ...data, fetchData: getDashboardData }}> <>
<Overview></Overview> <Overview></Overview>
<SystemLoad></SystemLoad> <SystemLoad></SystemLoad>
<Usage></Usage> <Usage></Usage>
<ActiveTable></ActiveTable> <ActiveTable></ActiveTable>
</DashboardContext.Provider> </>
); );
}; };
export default memo(Dashboard); export default Dashboard;
@@ -2,8 +2,7 @@ import LineChart from '@/components/echarts/line-chart';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import _ from 'lodash'; import _ from 'lodash';
import { memo, useContext, useMemo } from 'react'; import { memo, useMemo } from 'react';
import { DashboardContext } from '../config/dashboard-context';
const TypeKeyMap = { const TypeKeyMap = {
cpu: { cpu: {
@@ -32,9 +31,27 @@ const TypeKeyMap = {
} }
}; };
const UtilizationOvertime: React.FC = () => { const UtilizationOvertime: React.FC<{
data: {
cpu: {
timestamp: number;
value: number;
}[];
ram: {
timestamp: number;
value: number;
}[];
gpu: {
timestamp: number;
value: number;
}[];
vram: {
timestamp: number;
value: number;
}[];
};
}> = ({ data }) => {
const intl = useIntl(); const intl = useIntl();
const data = useContext(DashboardContext)?.system_load?.history || {};
const typeList = ['gpu', 'cpu', 'ram', 'vram']; const typeList = ['gpu', 'cpu', 'ram', 'vram'];
+52 -9
View File
@@ -1,10 +1,12 @@
import CardWrapper from '@/components/card-wrapper'; import CardWrapper from '@/components/card-wrapper';
import GaugeChart from '@/components/echarts/gauge'; import GaugeChart from '@/components/echarts/gauge';
import PageTools from '@/components/page-tools'; import PageTools from '@/components/page-tools';
import BaseSelect from '@/components/seal-form/base/select';
import { queryClusterList } from '@/pages/cluster-management/apis';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Col, Row } from 'antd'; import { Col, Row } from 'antd';
import _ from 'lodash'; import _ from 'lodash';
import { memo, useContext, useMemo } from 'react'; import { useContext, useEffect, useMemo, useState } from 'react';
import { DashboardContext } from '../config/dashboard-context'; import { DashboardContext } from '../config/dashboard-context';
import ResourceUtilization from './resource-utilization'; import ResourceUtilization from './resource-utilization';
@@ -14,9 +16,14 @@ const resourceChartHeight = 400;
const SystemLoad = () => { const SystemLoad = () => {
const intl = useIntl(); const intl = useIntl();
const data = useContext(DashboardContext)?.system_load?.current || {}; const { system_load, fetchData } = useContext(DashboardContext);
const [systemLoadData, setSystemLoadData] = useState<any>(system_load || {});
const [clusterList, setClusterList] = useState<Global.BaseOption<number>[]>(
[]
);
const chartData = useMemo(() => { const chartData = useMemo(() => {
const data = systemLoadData?.current || {};
return { return {
gpu: { gpu: {
data: _.round(data.gpu || 0, 1) data: _.round(data.gpu || 0, 1)
@@ -31,7 +38,36 @@ const SystemLoad = () => {
data: _.round(data.ram || 0, 1) data: _.round(data.ram || 0, 1)
} }
}; };
}, [data]); }, [systemLoadData?.current]);
useEffect(() => {
setSystemLoadData(system_load || {});
}, [system_load]);
const handleClusterChange = async (value: number) => {
try {
const res: any = await fetchData({ cluster_id: value });
setSystemLoadData(res.system_load || {});
} catch (error) {
setSystemLoadData({});
}
};
useEffect(() => {
const fetchClusters = async () => {
try {
const res = await queryClusterList({ page: 1, perPage: 100 });
const options = res.items.map((cluster) => ({
label: cluster.name,
value: cluster.id
}));
setClusterList(options);
} catch (error) {
setClusterList([]);
}
};
fetchClusters();
}, []);
return ( return (
<div> <div>
@@ -43,11 +79,22 @@ const SystemLoad = () => {
{intl.formatMessage({ id: 'dashboard.systemload' })} {intl.formatMessage({ id: 'dashboard.systemload' })}
</span> </span>
} }
right={
<BaseSelect
allowClear
onChange={handleClusterChange}
style={{ width: 360 }}
options={clusterList}
placeholder={intl.formatMessage({
id: 'clusters.filterBy.cluster'
})}
/>
}
/> />
<Row gutter={[20, 20]}> <Row gutter={[20, 20]}>
<Col xs={24} sm={24} md={24} lg={24} xl={16}> <Col xs={24} sm={24} md={24} lg={24} xl={16}>
<CardWrapper style={{ height: resourceChartHeight }}> <CardWrapper style={{ height: resourceChartHeight }}>
<ResourceUtilization /> <ResourceUtilization data={systemLoadData?.history} />
</CardWrapper> </CardWrapper>
</Col> </Col>
<Col xs={24} sm={24} md={24} lg={24} xl={8}> <Col xs={24} sm={24} md={24} lg={24} xl={8}>
@@ -55,7 +102,6 @@ const SystemLoad = () => {
<Row style={{ height: largeChartHeight }}> <Row style={{ height: largeChartHeight }}>
<Col span={12} style={{ height: smallChartHeight }}> <Col span={12} style={{ height: smallChartHeight }}>
<GaugeChart <GaugeChart
name="GPU"
height={smallChartHeight} height={smallChartHeight}
value={chartData.gpu.data} value={chartData.gpu.data}
title={intl.formatMessage({ title={intl.formatMessage({
@@ -65,7 +111,6 @@ const SystemLoad = () => {
</Col> </Col>
<Col span={12} style={{ height: smallChartHeight }}> <Col span={12} style={{ height: smallChartHeight }}>
<GaugeChart <GaugeChart
name="VRAM"
title={intl.formatMessage({ title={intl.formatMessage({
id: 'dashboard.vramutilization' id: 'dashboard.vramutilization'
})} })}
@@ -75,7 +120,6 @@ const SystemLoad = () => {
</Col> </Col>
<Col span={12} style={{ height: smallChartHeight }}> <Col span={12} style={{ height: smallChartHeight }}>
<GaugeChart <GaugeChart
name="CPU"
title={intl.formatMessage({ title={intl.formatMessage({
id: 'dashboard.cpuutilization' id: 'dashboard.cpuutilization'
})} })}
@@ -85,7 +129,6 @@ const SystemLoad = () => {
</Col> </Col>
<Col span={12} style={{ height: smallChartHeight }}> <Col span={12} style={{ height: smallChartHeight }}>
<GaugeChart <GaugeChart
name="RAM"
title={intl.formatMessage({ title={intl.formatMessage({
id: 'dashboard.memoryutilization' id: 'dashboard.memoryutilization'
})} })}
@@ -102,4 +145,4 @@ const SystemLoad = () => {
); );
}; };
export default memo(SystemLoad); export default SystemLoad;
@@ -2,7 +2,13 @@ import { createContext } from 'react';
import { DashboardProps } from './types'; import { DashboardProps } from './types';
export const DashboardContext = createContext< export const DashboardContext = createContext<
DashboardProps & { fetchData: () => Promise<void> } DashboardProps & {
>({} as DashboardProps & { fetchData: () => Promise<void> }); fetchData: (params?: { [key: string]: any }) => Promise<void>;
}
>(
{} as DashboardProps & {
fetchData: (params?: { [key: string]: any }) => Promise<void>;
}
);
export default DashboardContext; export default DashboardContext;
+27 -4
View File
@@ -1,19 +1,42 @@
import { useMemoizedFn } from 'ahooks';
import { Spin } from 'antd'; import { Spin } from 'antd';
import { useState } from 'react'; import { useEffect, useState } from 'react';
import PageBox from '../_components/page-box'; import PageBox from '../_components/page-box';
import { queryDashboardData } from './apis';
import DashboardInner from './components/dahboard-inner'; import DashboardInner from './components/dahboard-inner';
import DashboardContext from './config/dashboard-context';
import { DashboardProps } from './config/types';
const Dashboard: React.FC = () => { const Dashboard: React.FC = () => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [data, setData] = useState<DashboardProps>({} as DashboardProps);
const getDashboardData = useMemoizedFn(async () => {
try {
setLoading(true);
const res = await queryDashboardData();
setData(res);
setLoading(false);
} catch (error) {
setLoading(false);
setData({} as DashboardProps);
}
});
useEffect(() => {
getDashboardData();
}, []);
return ( return (
<> <DashboardContext.Provider
value={{ ...data, fetchData: queryDashboardData }}
>
<PageBox> <PageBox>
<Spin spinning={loading}> <Spin spinning={loading}>
<DashboardInner setLoading={setLoading} /> <DashboardInner />
</Spin> </Spin>
</PageBox> </PageBox>
</> </DashboardContext.Provider>
); );
}; };
+23 -2
View File
@@ -4,6 +4,7 @@ import SealSelect from '@/components/seal-form/seal-select';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Form } from 'antd'; import { Form } from 'antd';
import _ from 'lodash'; import _ from 'lodash';
import { useMemo } from 'react';
import { DeployFormKeyMap, modelCategories } from '../config'; import { DeployFormKeyMap, modelCategories } from '../config';
import { backendOptionsMap } from '../config/backend-parameters'; import { backendOptionsMap } from '../config/backend-parameters';
import { useFormContext } from '../config/form-context'; import { useFormContext } from '../config/form-context';
@@ -17,7 +18,11 @@ const AdvanceConfig = () => {
const form = Form.useFormInstance(); const form = Form.useFormInstance();
const EnviromentVars = Form.useWatch('env', form); const EnviromentVars = Form.useWatch('env', form);
const backend = Form.useWatch('backend', form); const backend = Form.useWatch('backend', form);
const { onValuesChange, formKey } = useFormContext(); const { onValuesChange, backendOptions, formKey } = useFormContext();
const currentBackendOptions = useMemo(() => {
return backendOptions?.find((item) => item.value === backend);
}, [backend, backendOptions]);
const handleEnviromentVarsChange = (labels: Record<string, any>) => { const handleEnviromentVarsChange = (labels: Record<string, any>) => {
console.log('handleEnviromentVarsChange', labels); console.log('handleEnviromentVarsChange', labels);
@@ -80,7 +85,23 @@ const AdvanceConfig = () => {
onChange={handleEnviromentVarsChange} onChange={handleEnviromentVarsChange}
></LabelSelector> ></LabelSelector>
</Form.Item> </Form.Item>
{(backend === backendOptionsMap.custom ||
!currentBackendOptions?.isBuiltIn) && (
<Form.Item<FormData>
name="cpu_offloading"
valuePropName="checked"
style={{ marginBottom: 8 }}
>
<CheckboxField
description={intl.formatMessage({
id: 'models.form.partialoffload.tips'
})}
label={intl.formatMessage({
id: 'resources.form.enablePartialOffload'
})}
></CheckboxField>
</Form.Item>
)}
{[backendOptionsMap.vllm, backendOptionsMap.ascendMindie].includes( {[backendOptionsMap.vllm, backendOptionsMap.ascendMindie].includes(
backend backend
) && ( ) && (
-1
View File
@@ -366,7 +366,6 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
replicas: 1, replicas: 1,
source: props.source, source: props.source,
placement_strategy: 'spread', placement_strategy: 'spread',
cpu_offloading: true,
scheduleType: ScheduleValueMap.Auto, scheduleType: ScheduleValueMap.Auto,
categories: null, categories: null,
restart_on_error: true, restart_on_error: true,