Skip to content

Commit

Permalink
Merge branch 'feat/react-progress-bar' of https://github.com/lablup/b…
Browse files Browse the repository at this point in the history
…ackend.ai-webui into feat/react-progress-bar
  • Loading branch information
ironAiken2 authored and yomybaby committed Aug 8, 2024
1 parent 65645e4 commit 0141095
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 33 deletions.
3 changes: 2 additions & 1 deletion react/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { useSuspendedBackendaiClient, useWebUINavigate } from './hooks';
import { useBAISettingUserState } from './hooks/useBAISetting';
import Page401 from './pages/Page401';
import Page404 from './pages/Page404';
import SummaryPage from './pages/SummaryPage';
import VFolderListPage from './pages/VFolderListPage';
import { Skeleton, theme } from 'antd';
import React, { Suspense } from 'react';
Expand Down Expand Up @@ -130,7 +131,7 @@ const router = createBrowserRouter([
style={{ marginBottom: token.paddingContentVerticalLG }}
closable
/>
{/* <SummaryPage /> */}
<SummaryPage />
</>
);
},
Expand Down
79 changes: 59 additions & 20 deletions react/src/components/BAIProgressBar.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,86 @@
import Flex from './Flex';
import { Progress, Typography, theme } from 'antd';
import { createStyles } from 'antd-style';
import _ from 'lodash';

const useStyles = createStyles(({ css }) => ({
progress: css`
const useStyles = createStyles(({ css }) => {
const defaultProgressCSS = css`
.ant-progress-inner {
border-radius: 4px !important;
}
.ant-progress-text {
margin-left: 6px;
}
`,
}));
`;
return {
progress: css`
${defaultProgressCSS}
`,
emptyProgress: css`
${defaultProgressCSS}
.ant-progress-bg {
background-color: unset !important;
}
`,
};
});

type ProgressData = {
current: number | string;
total: number | string;
};
interface BAIProgressBarProps {
title: string;
progressData: Array<ProgressData>;
max: number;
current: number;
prefix?: string;
showPercentage?: boolean;
color?: string;
style?: React.CSSProperties;
}

const BAIProgressBar: React.FC<BAIProgressBarProps> = ({
title,
progressData,
max,
current,
prefix,
showPercentage = false,
color,
style,
}) => {
const { token } = theme.useToken();
const { styles } = useStyles();
//TODO: percentage 계산을 내부에서 하는게 맞는지 확인
const percentage = max ? Math.round((current / max) * 100) : 0;

//TODO: progress 내부 텍스트만큼 백그라우드가 먹는 문제 해결

return (
<Flex justify="end" align="center" gap={token.marginLG}>
<Typography.Text strong style={{ flex: 1, textAlign: 'end' }}>
{title}
</Typography.Text>
<Flex gap={token.marginXS} style={{ width: 'inherit', ...style }}>
<Progress
className={styles.progress}
percent={50}
className={percentage ? styles.progress : styles.emptyProgress}
style={{
flex: 6,
fontSize: token.fontSizeSM,
}}
size={{ height: 18 }}
percent={percentage}
percentPosition={{ align: 'start', type: 'inner' }}
size={{ height: 20 }}
strokeLinecap="butt"
style={{ flex: 3 }}
strokeColor={color ?? token.colorPrimary}
format={() => (
<Typography.Text
style={{
fontSize: token.fontSizeSM,
color: percentage ? token.colorWhite : undefined,
}}
>
{current}/{max} {prefix}
</Typography.Text>
)}
/>
{showPercentage && (
<Typography.Text
type="secondary"
style={{ flex: 1, whiteSpace: 'nowrap', fontSize: token.fontSizeSM }}
>
{showPercentage && percentage + '%'}
</Typography.Text>
)}
</Flex>
);
};
Expand Down
156 changes: 147 additions & 9 deletions react/src/components/BAIResourcePanel.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,183 @@
import BAICard from '../BAICard';
import { bytesToBinarySize } from '../helper';
import {
useCurrentProjectValue,
useCurrentResourceGroupValue,
} from '../hooks/useCurrentProject';
import { useResourceLimitAndRemaining } from '../hooks/useResourceLimitAndRemaining';
import {
MergedResourceLimits,
ResourceLimits,
useResourceLimitAndRemaining,
} from '../hooks/useResourceLimitAndRemaining';
import BAIProgressBar from './BAIProgressBar';
import Flex from './Flex';
import ResourceGroupSelectForCurrentProject from './ResourceGroupSelectForCurrentProject';
import { CardProps, Typography } from 'antd';
import { Badge, CardProps, Typography, theme } from 'antd';
import _ from 'lodash';
import { useTranslation } from 'react-i18next';

const deviceNameMap = {
cpu: 'CPU',
mem: 'RAM',
'cuda.device': 'GPU',
'cuda.shares': 'FGPU',
'rocm.device': 'ROCm GPU',
'tpu.device': 'TPU',
'ipu.device': 'IPU',
'atom.device': 'ATOM',
'atom-plus.device': 'ATOM+',
'warboy.device': 'Warboy',
'hyperaccel-lpu.device': 'Hyperaccel LPU',
};
interface BAIResourcePanelProps extends CardProps {
width?: number;
height?: number;
width?: number | string;
height?: number | string;
}

const BAIResourcePanel: React.FC<BAIResourcePanelProps> = ({
width,
height,
}) => {
const { t } = useTranslation();
const { token } = theme.useToken();
const currentProject = useCurrentProjectValue();
const currentResourceGroup = useCurrentResourceGroupValue();
const fetchData = useResourceLimitAndRemaining({
currentProjectName: currentProject.name,
currentResourceGroup: currentResourceGroup || 'default',
});

console.log(fetchData);
})[0];
const deviceKeys = _.keysIn(fetchData?.checkPresetInfo?.keypair_limits || {});

return (
<BAICard
style={{ width: width, height: height }}
style={{ width: width }}
title={
<Flex justify="between">
<Typography.Text>{t('summary.ResourceStatistics')}</Typography.Text>
<ResourceGroupSelectForCurrentProject />
</Flex>
}
>
<BAIProgressBar title="CPU" progressData={[]} />
<Flex
align="start"
direction="column"
gap={token.marginMD}
style={{ width: '100%', height: height, overflowY: 'scroll' }}
>
<Flex
direction="column"
align="start"
style={{
position: 'sticky',
top: 0,
zIndex: 1,
backgroundColor: token.colorWhite,
width: '100%',
}}
>
<Flex gap={token.marginXS}>
<Badge color={token.blue7} />
<Typography.Text type="secondary">
{t('session.launcher.CurrentResourceGroup')} (
{currentResourceGroup})
</Typography.Text>
</Flex>
<Flex gap={token.marginXS}>
<Badge color={token.green7} />
<Typography.Text type="secondary">
{t('session.launcher.UserResourceLimit')}
</Typography.Text>
</Flex>
</Flex>
{_.map(deviceKeys, (device: keyof ResourceLimits) => {
const total_resource_slot =
fetchData?.resourceLimits[device as keyof MergedResourceLimits]
?.max ||
fetchData?.resourceLimits.accelerators[
device as keyof MergedResourceLimits
]?.max;
//TODO: 각가의 변수명이 제대로 매칭되어 있는지 확인
const scaling_groups_using =
fetchData?.checkPresetInfo?.keypair_using[device];
const used_resource_group_slot =
fetchData?.checkPresetInfo?.scaling_groups[
currentResourceGroup || 'default'
].using[device];

return (
<Flex
gap={token.marginMD}
align="center"
justify="center"
style={{ width: 'inherit' }}
>
<Typography.Text
strong
style={{
flex: 1,
display: 'flex',
justifyContent: 'end',
wordBreak: 'break-all',
}}
>
{deviceNameMap[device]}
</Typography.Text>
<Flex
gap={token.marginXXS}
direction="column"
style={{ flex: 4, width: '100%' }}
>
<BAIProgressBar
max={
device === 'mem'
? bytesToBinarySize(
parseFloat(total_resource_slot as string),
'GiB',
).number
: Number(total_resource_slot)
}
current={
used_resource_group_slot
? device === 'mem'
? bytesToBinarySize(
parseFloat(used_resource_group_slot as string),
'GiB',
).number
: Number(used_resource_group_slot)
: 0
}
prefix={device === 'mem' ? 'GiB' : undefined}
showPercentage
color={token.blue7}
/>
<BAIProgressBar
max={
device === 'mem'
? bytesToBinarySize(
parseFloat(total_resource_slot as string),
'GiB',
).number
: Number(total_resource_slot)
}
current={
scaling_groups_using
? device === 'mem'
? bytesToBinarySize(
parseFloat(scaling_groups_using as string),
'GiB',
).number
: Number(scaling_groups_using)
: 0
}
prefix={device === 'mem' ? 'GiB' : undefined}
showPercentage
color={token.green7}
/>
</Flex>
</Flex>
);
})}
</Flex>
</BAICard>
);
};
Expand Down
4 changes: 2 additions & 2 deletions react/src/hooks/useResourceLimitAndRemaining.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useResourceSlots } from '../hooks/backendai';
import { useTanQuery } from './reactQueryAlias';
import _ from 'lodash';

interface MergedResourceLimits {
export interface MergedResourceLimits {
accelerators: {
[key: string]:
| {
Expand All @@ -29,7 +29,7 @@ interface MergedResourceLimits {
};
}

type ResourceLimits = {
export type ResourceLimits = {
cpu: string | 'Infinity' | 'NaN';
mem: string | 'Infinity' | 'NaN';
'cuda.device': string | 'Infinity' | 'NaN';
Expand Down
23 changes: 23 additions & 0 deletions react/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import App from './App';
import BAIIntervalText from './components/BAIIntervalText';
import BAIResourcePanel from './components/BAIResourcePanel';
import { jotaiStore, useWebComponentInfo } from './components/DefaultProviders';
import Flex from './components/Flex';
import FlexActivityIndicator from './components/FlexActivityIndicator';
Expand Down Expand Up @@ -319,6 +320,28 @@ customElements.define(
}),
);

customElements.define(
'backend-ai-react-resource-panel',
reactToWebComponent((props) => {
return (
<DefaultProviders {...props}>
<ReactResourcePanel {...props} />
</DefaultProviders>
);
}),
);

const ReactResourcePanel = (props: ReactWebComponentProps) => {
const { parsedValue } = useWebComponentInfo<{
width?: number;
height?: number;
}>();

return (
<BAIResourcePanel width={parsedValue.width} height={parsedValue.height} />
);
};

customElements.define(
'backend-ai-session-reservation-timer',
reactToWebComponent((props) => {
Expand Down
2 changes: 1 addition & 1 deletion react/src/pages/SummaryPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const SummaryPage: React.FC = () => {
);
}}
/>
<BAIResourcePanel />
<BAIResourcePanel width={350} height={'420px'} />
{/* @ts-ignore */}
<backend-ai-summary-view active></backend-ai-summary-view>
</>
Expand Down

0 comments on commit 0141095

Please sign in to comment.