Skip to content

Commit

Permalink
feat: new resources page
Browse files Browse the repository at this point in the history
  • Loading branch information
agatha197 committed May 9, 2024
1 parent 5e3a4ee commit f730559
Show file tree
Hide file tree
Showing 40 changed files with 1,499 additions and 129 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
"patch-package": "^8.0.0",
"tus-js-client": "^4.1.0",
"utf-8-validate": "^6.0.3",
"uuid": "^9.0.1",
"vis-data": "^7.1.9",
"vis-network": "^9.1.9",
"vis-util": "^5.0.7",
Expand Down
32 changes: 25 additions & 7 deletions react/data/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,19 @@ type Queries {

"""Added in 24.03.0."""
group_nodes(filter: String, order: String, offset: Int, before: String, after: String, first: Int, last: Int): GroupConnection
group(id: UUID!, domain_name: String): Group
group(
id: UUID!
domain_name: String

"""Added in 24.03.0."""
type: [String] = ["GENERAL"]
): Group
groups_by_name(name: String!, domain_name: String): [Group]
groups(
domain_name: String
is_active: Boolean

"""Added since 24.03.0. Available values: GENERAL, MODEL_STORE"""
"""Added in 24.03.0."""
type: [String] = ["GENERAL"]
): [Group]
image(
Expand All @@ -39,7 +45,7 @@ type Queries {
): Image
images(is_installed: Boolean, is_operation: Boolean): [Image]

"""Added since 24.03.1"""
"""Added in 24.03.1"""
customized_images: [ImageNode]
user(domain_name: String, email: String): User
user_from_uuid(domain_name: String, user_id: ID): User
Expand Down Expand Up @@ -166,6 +172,9 @@ type ComputeContainer implements Item {
idx: Int
role: String
hostname: String

"""Added in 24.03.1."""
kernel_id: UUID
cluster_idx: Int
local_rank: Int
cluster_role: String
Expand Down Expand Up @@ -999,7 +1008,7 @@ type Mutations {
unload_image(references: [String]!, target_agents: [String]!): UnloadImage
modify_image(architecture: String = "x86_64", props: ModifyImageInput!, target: String!): ModifyImage

"""Added since 24.03.0"""
"""Added in 24.03.0"""
forget_image_by_id(image_id: String!): ForgetImageById
forget_image(architecture: String = "x86_64", reference: String!): ForgetImage

Expand Down Expand Up @@ -1104,7 +1113,7 @@ type CreateGroup {
}

input GroupInput {
"""Added since 24.03.0. Available values: GENERAL, MODEL_STORE"""
"""Added in 24.03.0."""
type: String = "GENERAL"
description: String = ""
is_active: Boolean = true
Expand All @@ -1114,7 +1123,7 @@ input GroupInput {
integration_id: String = ""
resource_policy: String = "default"

"""Added since 24.03.0"""
"""Added in 24.03.0"""
container_registry: JSONString = "{}"
}

Expand All @@ -1136,7 +1145,7 @@ input ModifyGroupInput {
integration_id: String
resource_policy: String

"""Added since 24.03.0"""
"""Added in 24.03.0"""
container_registry: JSONString = "{}"
}

Expand Down Expand Up @@ -1323,17 +1332,26 @@ input ResourceLimitInput {
type ForgetImageById {
ok: Boolean
msg: String

"""Added since 24.03.1."""
image: ImageNode
}

type ForgetImage {
ok: Boolean
msg: String

"""Added since 24.03.1."""
image: ImageNode
}

"""Added in 24.03.1"""
type UntagImageFromRegistry {
ok: Boolean
msg: String

"""Added since 24.03.1."""
image: ImageNode
}

type AliasImage {
Expand Down
2 changes: 2 additions & 0 deletions react/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const SessionLauncherPage = React.lazy(
const NeoSessionLauncherSwitchAlert = React.lazy(
() => import('./components/NeoSessionLauncherSwitchAlert'),
);
const ResourcesPage = React.lazy(() => import('./pages/ResourcesPage'));

const router = createBrowserRouter([
{
Expand Down Expand Up @@ -152,6 +153,7 @@ const router = createBrowserRouter([
{
path: '/agent',
handle: { labelKey: 'webui.menu.ComputationResources' },
Component: ResourcesPage,
},
{
path: '/settings',
Expand Down
214 changes: 214 additions & 0 deletions react/src/components/AgentDetailModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
import { iSizeToSize } from '../helper';
import { useResourceSlotsDetails } from '../hooks/backendai';
import BAIModal, { BAIModalProps } from './BAIModal';
import BAIProgressWithLabel from './BAIProgressWithLabel';
import Flex from './Flex';
import { AgentDetailModalFragment$key } from './__generated__/AgentDetailModalFragment.graphql';
import { Col, Row, Typography } from 'antd';
import graphql from 'babel-plugin-relay/macro';
import _ from 'lodash';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useFragment } from 'react-relay';

type LiveStat = {
capacity: string;
current: string;
pct: string;
'stats.avg': string;
'stats.max': string;
unit_hint: string;
};

interface AgentDetailModalProps extends BAIModalProps {
agentDetailModalFrgmt?: AgentDetailModalFragment$key | null;
onRequestClose: () => void;
}

const AgentDetailModal: React.FC<AgentDetailModalProps> = ({
agentDetailModalFrgmt = null,
onRequestClose,
...modalProps
}) => {
const { t } = useTranslation();
const [resourceSlotsDetails] = useResourceSlotsDetails();
const agent = useFragment(
graphql`
fragment AgentDetailModalFragment on Agent {
id
live_stat
available_slots
occupied_slots
cpu_cur_pct
mem_cur_bytes
}
`,
agentDetailModalFrgmt,
);
const parsedLiveStat = JSON.parse(agent?.live_stat || '{}');
const parsedAvailableSlots = JSON.parse(agent?.available_slots || '{}');

return (
<BAIModal
{...modalProps}
centered
title={`${t('agent.DetailedInformation')}: ${agent?.id}`}
onCancel={onRequestClose}
destroyOnClose
footer={<></>}
>
<Row gutter={[16, 16]}>
<Col xs={{ flex: '100%' }} sm={{ flex: '50%' }}>
{parsedLiveStat?.devices?.cpu_util ? (
<Flex direction="column" gap="xxs" align="stretch">
<Typography.Title level={5}>
{resourceSlotsDetails?.cpu?.human_readable_name}
</Typography.Title>
{_.map(parsedLiveStat?.devices?.cpu_util, (value, key) => (
<Flex justify="between">
<Typography.Text
key={key}
type="secondary"
style={{ flex: 0.5 }}
>
{resourceSlotsDetails?.cpu?.human_readable_name}
{key}
</Typography.Text>
<BAIProgressWithLabel
percent={value?.pct}
valueLabel={value?.pct + '%'}
/>
</Flex>
))}
</Flex>
) : null}
</Col>
<Col xs={{ flex: '100%' }} sm={{ flex: '50%' }}>
{parsedAvailableSlots?.mem ? (
<Flex direction="column" gap="xxs" align="stretch">
<Typography.Title level={5}>
{resourceSlotsDetails?.mem?.human_readable_name}
</Typography.Title>
<BAIProgressWithLabel
percent={
((iSizeToSize(_.toString(agent?.mem_cur_bytes), 'g')
?.number ?? 0) /
(iSizeToSize(parsedAvailableSlots?.mem, 'g')?.number ??
0)) *
100 ?? 0
}
valueLabel={`${
iSizeToSize(_.toString(agent?.mem_cur_bytes), 'g')?.numberUnit
}iB / ${iSizeToSize(parsedAvailableSlots?.mem, 'g')?.numberUnit}iB`}
/>
</Flex>
) : null}
{parsedLiveStat?.node ? (
<Flex direction="column" gap="xxs" align="start">
<Typography.Title level={5}>
{t('session.launcher.Network')}
</Typography.Title>
<Flex gap="xl">
<Typography.Text>TX:</Typography.Text>
<Typography.Text>
{
iSizeToSize(parsedLiveStat?.node?.net_tx?.current, 'm', 1)
?.numberUnit
}
iB
</Typography.Text>
</Flex>
<Flex gap="xl">
<Typography.Text>RX:</Typography.Text>
<Typography.Text>
{
iSizeToSize(parsedLiveStat?.node?.net_rx?.current, 'm', 1)
?.numberUnit
}
iB
</Typography.Text>
</Flex>
</Flex>
) : null}
</Col>
</Row>
<Row gutter={[16, 16]}>
{_.map(_.keys(parsedLiveStat?.devices), (key) => {
if (['cpu_util', 'mem', 'disk', 'net_rx', 'net_tx'].includes(key)) {
return null;
} else if (_.includes(key, '_util')) {
const deviceName = _.split(key, '_')[0] + '.device';
return (
<Col xs={{ flex: '100%' }} sm={{ flex: '50%' }}>
<Flex direction="column" gap="xxs" align="stretch">
<Typography.Title level={5}>
{resourceSlotsDetails?.[deviceName]?.human_readable_name}{' '}
{t('session.Utilization')}
</Typography.Title>
{_.map(
_.toPairs(parsedLiveStat?.devices[key]),
(value, index) => (
<Flex justify="between">
<Typography.Text
key={index}
type="secondary"
style={{ flex: 0.5 }}
>
{
resourceSlotsDetails?.[deviceName]
?.human_readable_name
}
{index}
</Typography.Text>
<BAIProgressWithLabel
percent={_.toFinite((value?.[1] as LiveStat)?.pct)}
valueLabel={(value?.[1] as LiveStat)?.pct + '%'}
/>
</Flex>
),
)}
</Flex>
</Col>
);
} else if (_.includes(key, '_mem')) {
const deviceName = _.split(key, '_')[0] + '.device';
return (
<Col xs={{ flex: '100%' }} sm={{ flex: '50%' }}>
<Flex direction="column" gap="xxs" align="stretch">
<Typography.Title level={5}>
{resourceSlotsDetails?.[deviceName]?.human_readable_name}{' '}
{t('session.launcher.Memory')}
</Typography.Title>
{_.map(
_.toPairs(parsedLiveStat?.devices[key]),
(value, index) => (
<Flex justify="between">
<Typography.Text
key={index}
type="secondary"
style={{ flex: 0.5 }}
>
{
resourceSlotsDetails?.[deviceName]
?.human_readable_name
}
{index}
</Typography.Text>
<BAIProgressWithLabel
percent={_.toFinite((value?.[1] as LiveStat)?.pct)}
valueLabel={(value?.[1] as LiveStat)?.pct + '%'}
/>
</Flex>
),
)}
</Flex>
</Col>
);
}
})}
</Row>
</BAIModal>
);
};

export default AgentDetailModal;
Loading

0 comments on commit f730559

Please sign in to comment.