feat: monitor button

This commit is contained in:
jialin
2026-02-04 20:14:25 +08:00
parent 47596fdb1a
commit 4e8b5e26ae
40 changed files with 352 additions and 176 deletions
+2
View File
@@ -7,6 +7,7 @@ import {
queryVersionInfo,
updateCheck
} from '@/services/profile/apis';
import { fetchSystemConfig } from '@/services/system/query-system-config';
import { isOnline } from '@/utils';
import {
IS_FIRST_LOGIN,
@@ -57,6 +58,7 @@ export async function getInitialState(): Promise<{
});
if (data.is_admin) {
getUpdateCheck();
fetchSystemConfig();
}
return data;
} catch (error: any) {
+12
View File
@@ -0,0 +1,12 @@
import { atom } from 'jotai';
export interface SystemConfig {
disable_builtin_observability: boolean;
debug: boolean;
grafana_url: string;
server_external_url: string | null;
system_default_container_registry: string | null;
showMonitoring?: boolean;
}
export const systemConfigAtom = atom<SystemConfig>({} as SystemConfig);
+1 -1
View File
@@ -2,7 +2,7 @@ import { createFromIconfontCN } from '@ant-design/icons';
// import './iconfont/iconfont.js';
const IconFont = createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/c/font_4613488_n2otiiv2h4s.js'
scriptUrl: '//at.alicdn.com/t/c/font_4613488_c3lajf3mdtk.js'
});
export default IconFont;
+1 -1
View File
@@ -9,7 +9,7 @@ export default {
'ai.provider.cohere': 'Cohere',
'ai.provider.coze': 'Coze',
'ai.provider.deepl': 'DeepL',
'ai.provider.deepseek': 'Deepseek',
'ai.provider.deepseek': 'DeepSeek',
'ai.provider.dify': 'Dify',
'ai.provider.doubao': 'Doubao',
'ai.provider.fireworks': 'Fireworks',
+1 -1
View File
@@ -2,7 +2,7 @@ export default {
'models.button.deploy': 'Deploy Model',
'models.title': 'Models',
'models.title.edit': 'Edit Model',
'models.title.duplicate': 'Duplicate Model',
'models.title.duplicate': 'Clone Model',
'models.table.models': 'models',
'models.table.name': 'Model Name',
'models.form.source': 'Source',
+1
View File
@@ -1,6 +1,7 @@
export default {
'providers.title': 'Providers',
'providers.button.add': 'Add Provider',
'providers.button.clone': 'Clone Provider',
'providers.table.providerName': 'Provider',
'providers.table.models': 'Models',
'providers.table.proxy': 'Proxy',
+1 -1
View File
@@ -9,7 +9,7 @@ export default {
'ai.provider.cohere': 'Cohere',
'ai.provider.coze': 'Coze',
'ai.provider.deepl': 'DeepL',
'ai.provider.deepseek': 'Deepseek',
'ai.provider.deepseek': 'DeepSeek',
'ai.provider.dify': 'Dify',
'ai.provider.doubao': 'Doubao',
'ai.provider.fireworks': 'Fireworks',
+1 -1
View File
@@ -2,7 +2,7 @@ export default {
'models.button.deploy': 'モデルをデプロイ',
'models.title': 'モデル',
'models.title.edit': 'モデルを編集',
'models.title.duplicate': 'モデルを複製',
'models.title.duplicate': 'モデルをクローン',
'models.table.models': 'モデル',
'models.table.name': 'モデル名',
'models.form.source': 'ソース',
+1
View File
@@ -1,6 +1,7 @@
export default {
'providers.title': 'Providers',
'providers.button.add': 'Add Provider',
'providers.button.clone': 'Clone Provider',
'providers.table.providerName': 'Provider',
'providers.table.models': 'Models',
'providers.table.proxy': 'Proxy',
+1 -1
View File
@@ -9,7 +9,7 @@ export default {
'ai.provider.cohere': 'Cohere',
'ai.provider.coze': 'Coze',
'ai.provider.deepl': 'DeepL',
'ai.provider.deepseek': 'Deepseek',
'ai.provider.deepseek': 'DeepSeek',
'ai.provider.dify': 'Dify',
'ai.provider.doubao': 'Doubao',
'ai.provider.fireworks': 'Fireworks',
+1 -1
View File
@@ -2,7 +2,7 @@ export default {
'models.button.deploy': 'Развернуть модель',
'models.title': 'Модели',
'models.title.edit': 'Редактировать модель',
'models.title.duplicate': 'Дублировать модель',
'models.title.duplicate': 'Клонировать модель',
'models.table.models': 'Модели',
'models.table.name': 'Название модели',
'models.form.source': 'Источник',
+1
View File
@@ -1,6 +1,7 @@
export default {
'providers.title': 'Providers',
'providers.button.add': 'Add Provider',
'providers.button.clone': 'Clone Provider',
'providers.table.providerName': 'Provider',
'providers.table.models': 'Models',
'providers.table.proxy': 'Proxy',
+1 -1
View File
@@ -9,7 +9,7 @@ export default {
'ai.provider.cohere': 'Cohere',
'ai.provider.coze': '扣子',
'ai.provider.deepl': 'DeepL',
'ai.provider.deepseek': '深度求索',
'ai.provider.deepseek': 'DeepSeek',
'ai.provider.dify': 'Dify',
'ai.provider.doubao': '豆包',
'ai.provider.fireworks': 'Fireworks AI',
+1 -1
View File
@@ -2,7 +2,7 @@ export default {
'models.button.deploy': '部署模型',
'models.title': '模型',
'models.title.edit': '编辑模型',
'models.title.duplicate': '复制模型',
'models.title.duplicate': '克隆模型',
'models.table.models': '模型',
'models.table.name': '模型名称',
'models.form.source': '来源',
+1
View File
@@ -1,6 +1,7 @@
export default {
'providers.title': '提供商',
'providers.button.add': '添加提供商',
'providers.button.clone': '克隆提供商',
'providers.table.providerName': '提供商',
'providers.table.models': '模型',
'providers.table.proxy': '代理',
+1 -1
View File
@@ -6,5 +6,5 @@ export default {
'vendor.iluvatar': '天数智芯',
'vendor.metax': '沐曦',
'vendor.cambricon': '寒武纪',
'vendor.thead': '平头哥 (PPU)'
'vendor.thead': '平头哥 PPU'
};
@@ -1,7 +1,9 @@
import { systemConfigAtom } from '@/atoms/system';
import { PageAction } from '@/config';
import { PageActionType } from '@/config/types';
import ColumnWrapper from '@/pages/_components/column-wrapper';
import { useIntl } from '@umijs/max';
import { useAtom } from 'jotai';
import _ from 'lodash';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import styled from 'styled-components';
@@ -23,7 +25,6 @@ import { ProviderType, ProviderValueMap } from './config';
import providerList from './config/providers';
import { StepsContext } from './config/steps-context';
import { ClusterFormData } from './config/types';
import useSystemConfig from './services/use-system-config';
import { moduleMap, moduleRegistry } from './step-forms/module-registry';
import useStepList from './step-forms/use-step-list';
@@ -59,7 +60,7 @@ const ClusterCreate: React.FC<{
}> = ({ onClose, action, setCurrentTitle }) => {
const startStep = 0;
const stepList = useStepList();
const { systemConfig } = useSystemConfig();
const [systemConfigState] = useAtom(systemConfigAtom);
const intl = useIntl();
const [credentialList, setCredentialList] = useState<
Global.BaseOption<number, { provider: ProviderType }>[]
@@ -361,7 +362,7 @@ const ClusterCreate: React.FC<{
<StepsContext.Provider
value={{
formValues: formValues,
systemConfig: systemConfig
systemConfig: systemConfigState
}}
>
{renderModules()}
+16
View File
@@ -19,6 +19,7 @@ import _ from 'lodash';
import { useEffect, useState } from 'react';
import NoResult from '../_components/no-result';
import PageBox from '../_components/page-box';
import useGranfanaLink from '../resources/hooks/use-grafana-link';
import {
CLUSTERS_API,
createWorkerPool,
@@ -38,6 +39,7 @@ import {
K8sStepsFromCluter
} from './components/add-worker/config';
import PoolRows from './components/pool-rows';
import RightActions from './components/right-actions';
import { ProviderType, ProviderValueMap } from './config';
import {
ClusterListItem,
@@ -71,6 +73,10 @@ const Clusters: React.FC = () => {
contentForDelete: 'menu.clusterManagement.clusters'
});
const navigate = useNavigate();
const { goToGrafana, ActionButton } = useGranfanaLink({
type: 'cluster',
dataList: dataSource.dataList || []
});
const { watchDataList: allWorkerPoolList } = useWatchList(WORKER_POOLS_API);
const [expandAtom] = useAtom(expandKeysAtom);
const [clusterSession, setClusterSession] = useAtom(clusterSessionAtom);
@@ -196,6 +202,8 @@ const Clusters: React.FC = () => {
setDefaultCluster({ id: row.id }).then(() => {
message.success(intl.formatMessage({ id: 'common.message.success' }));
});
} else if (val === 'metrics') {
goToGrafana(row);
}
});
@@ -337,6 +345,14 @@ const Clusters: React.FC = () => {
handleSearch={handleSearch}
handleDeleteByBatch={handleDeleteBatch}
handleClickPrimary={handleClickDropdown}
right={
<RightActions
handleDeleteByBatch={handleDeleteBatch}
handleClickPrimary={handleClickDropdown}
rowSelection={rowSelection}
MonitorButton={ActionButton()}
></RightActions>
}
></FilterBar>
<TableContext.Provider
value={{
@@ -0,0 +1,50 @@
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Button, Space } from 'antd';
import React from 'react';
export interface RightActionsProps {
handleDeleteByBatch: () => void;
handleClickPrimary?: () => void;
MonitorButton?: React.ReactNode;
rowSelection: {
selectedRowKeys: React.Key[];
};
}
const RightActions: React.FC<RightActionsProps> = ({
handleDeleteByBatch,
handleClickPrimary,
rowSelection,
MonitorButton
}) => {
const intl = useIntl();
return (
<Space size={16}>
{MonitorButton}
<Button
icon={<PlusOutlined />}
type="primary"
onClick={handleClickPrimary}
>
{intl.formatMessage({ id: 'clusters.button.add' })}
</Button>
<Button
icon={<DeleteOutlined />}
danger
onClick={handleDeleteByBatch}
disabled={!rowSelection?.selectedRowKeys?.length}
>
<span>
{intl.formatMessage({ id: 'common.button.delete' })}
{rowSelection?.selectedRowKeys?.length > 0 && (
<span>({rowSelection?.selectedRowKeys?.length})</span>
)}
</span>
</Button>
</Space>
);
};
export default RightActions;
@@ -94,6 +94,11 @@ export const clusterActionList = [
label: 'common.button.edit',
icon: icons.EditOutlined
},
{
label: 'resources.metrics.details',
key: 'metrics',
icon: icons.Metrics
},
{
key: 'add_worker',
label: 'resources.button.create',
+5 -2
View File
@@ -84,7 +84,10 @@ export interface ClusterFormData {
}
export interface SystemConfig {
disable_builtin_observability?: boolean;
debug: boolean;
server_external_url: string;
system_default_container_registry: string;
grafana_url?: string;
server_external_url: string | null;
system_default_container_registry: string | null;
showMonitoring?: boolean;
}
@@ -1,4 +1,5 @@
// columns.ts
import { systemConfigAtom } from '@/atoms/system';
import AutoTooltip from '@/components/auto-tooltip';
import DropdownButtons from '@/components/drop-down-buttons';
import { SealColumnProps } from '@/components/seal-table/types';
@@ -8,6 +9,7 @@ import { StarFilled } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Tooltip } from 'antd';
import dayjs from 'dayjs';
import { useAtomValue } from 'jotai';
import { useMemo } from 'react';
import {
ClusterStatus,
@@ -17,20 +19,24 @@ import {
} from '../config';
import { ClusterListItem } from '../config/types';
const setActionsItems = (row: ClusterListItem) => {
return clusterActionList.filter((item) => {
if (item.provider) {
return item.provider === row.provider;
}
return true;
});
};
const useClusterColumns = (
handleSelect: (val: string, record: ClusterListItem) => void,
onCellClick?: (record: ClusterListItem, dataIndex: string) => void
): SealColumnProps[] => {
const intl = useIntl();
const systemConfig = useAtomValue(systemConfigAtom);
const setActionsItems = (row: ClusterListItem) => {
return clusterActionList.filter((item) => {
if (item.provider) {
return item.provider === row.provider;
}
if (item.key === 'metrics') {
return systemConfig?.showMonitoring;
}
return true;
});
};
return useMemo(() => {
return [
@@ -42,9 +48,6 @@ const useClusterColumns = (
render: (text: string, record: ClusterListItem) => (
<>
<AutoTooltip ghost title={text}>
{/* <Typography.Link onClick={() => onCellClick?.(record, 'name')}>
{record.name}
</Typography.Link> */}
{text}
</AutoTooltip>
{record.is_default && (
@@ -1,3 +1,4 @@
import { systemConfigAtom } from '@/atoms/system';
import AutoTooltip from '@/components/auto-tooltip';
import DropdownButtons from '@/components/drop-down-buttons';
import IconFont from '@/components/icon-font';
@@ -24,6 +25,7 @@ import {
import { useIntl } from '@umijs/max';
import { Button, Col, Progress, Row, Tooltip, notification } from 'antd';
import dayjs from 'dayjs';
import { useAtomValue } from 'jotai';
import _ from 'lodash';
import React, { useCallback, useEffect, useMemo } from 'react';
import styled from 'styled-components';
@@ -435,6 +437,7 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
defaultOpenId,
handleChildSelect
}) => {
const systemConfig = useAtomValue(systemConfigAtom);
const { goToGrafana } = useGranfanaLink({ type: 'instance' });
const { runBenchmarkOnInstance } = useBenchmarkTargetInstance();
const [api, contextHolder] = notification.useNotification({
@@ -453,9 +456,12 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
if (action.status && action.status.length > 0) {
return action.status.includes(instanceData.state);
}
if (action.key === 'metrics') {
return systemConfig.showMonitoring;
}
return true;
});
}, [instanceData.state, modelData]);
}, [instanceData.state, modelData, systemConfig.showMonitoring]);
const createFileName = (name: string) => {
const timestamp = dayjs().format('YYYY-MM-DD_HH-mm-ss');
+5 -2
View File
@@ -9,6 +9,7 @@ import SealTable from '@/components/seal-table';
import { TableOrder } from '@/components/seal-table/types';
import { PageAction } from '@/config';
import { TABLE_SORT_DIRECTIONS } from '@/config/settings';
import { PageActionType } from '@/config/types';
import useBodyScroll from '@/hooks/use-body-scroll';
import useExpandedRowKeys from '@/hooks/use-expanded-row-keys';
import useTableRowSelection from '@/hooks/use-table-row-selection';
@@ -170,8 +171,9 @@ const Models: React.FC<ModelsProps> = ({
useEffect(() => {
fetchTargets({});
}, []);
const { goToGrafana } = useGranfanaLink({
type: 'model'
const { goToGrafana, ActionButton } = useGranfanaLink({
type: 'model',
dataList: dataSource || []
});
const [openLogModal, setOpenLogModal] = useState(false);
@@ -660,6 +662,7 @@ const Models: React.FC<ModelsProps> = ({
}
right={
<Space size={16}>
{ActionButton()}
{page !== 'clusters' && (
<DropDownActions
menu={{
+6 -6
View File
@@ -39,11 +39,6 @@ export const ActionList: ActionItem[] = [
key: 'edit',
icon: icons.EditOutlined
},
{
label: 'resources.metrics.details',
key: 'metrics',
icon: icons.Metrics
},
{
label: 'models.openinplayground',
key: 'chat',
@@ -51,7 +46,7 @@ export const ActionList: ActionItem[] = [
},
{
key: 'copy',
label: 'common.button.duplicate',
label: 'common.button.clone',
icon: icons.CopyOutlined
},
{
@@ -64,6 +59,11 @@ export const ActionList: ActionItem[] = [
key: 'stop',
icon: icons.Stop
},
{
label: 'resources.metrics.details',
key: 'metrics',
icon: icons.Metrics
},
{
label: 'common.button.delete',
key: 'delete',
+1 -1
View File
@@ -38,7 +38,7 @@ const BackendFields: React.FC = () => {
const [showDeprecated, setShowDeprecated] = React.useState<boolean>(false);
const handleBackendVersionOnChange = (value: any, option: any) => {
if (Object.keys(option.data.env || {}).length > 0) {
if (Object.keys(option.data?.env || {}).length > 0) {
form.setFieldValue('env', { ...(option?.data?.env || {}) });
}
@@ -34,7 +34,8 @@ const useEditDeployment = () => {
currentData: {
isGGUF: false,
data: formData,
row: row
row: row,
realAction: PageAction.EDIT
},
title: intl.formatMessage({ id: 'models.title.edit' })
});
@@ -1,4 +1,5 @@
// columns.ts
import { systemConfigAtom } from '@/atoms/system';
import AutoTooltip from '@/components/auto-tooltip';
import DropdownButtons from '@/components/drop-down-buttons';
import { SealColumnProps } from '@/components/seal-table/types';
@@ -9,6 +10,7 @@ import { useIntl } from '@umijs/max';
import { useMemoizedFn } from 'ahooks';
import { Tooltip } from 'antd';
import dayjs from 'dayjs';
import { useAtomValue } from 'jotai';
import _ from 'lodash';
import { useMemo } from 'react';
import ModelTag from '../../_components/model-tag';
@@ -31,6 +33,7 @@ const useModelsColumns = ({
targetList
}: ModelsColumnsHookProps & { targetList: any[] }): SealColumnProps[] => {
const intl = useIntl();
const systemConfig = useAtomValue(systemConfigAtom);
const setModelActionList = useMemoizedFn((record: any) => {
return _.filter(ActionList, (action: any) => {
@@ -52,6 +55,9 @@ const useModelsColumns = ({
if (action.key === 'stop') {
return record.replicas > 0;
}
if (action.key === 'metrics') {
return systemConfig.showMonitoring;
}
return true;
});
+3 -3
View File
@@ -146,15 +146,15 @@ const UserModels: React.FC = () => {
};
const getStatus = useCallback((model: any) => {
if (!model.replicas && !model.ready_endpoints) {
if (!model.targets && !model.ready_targets) {
return MyModelsStatusValueMap.Inactive;
}
if (model.replicas > 0 && !model.ready_endpoints) {
if (model.targets > 0 && !model.ready_targets) {
return MyModelsStatusValueMap.Degrade;
}
if (model.ready_endpoints > 0 && model.replicas > 0) {
if (model.ready_targets > 0 && model.targets > 0) {
return MyModelsStatusValueMap.Active;
}
return MyModelsStatusValueMap.Degrade;
+2
View File
@@ -1,4 +1,5 @@
import { hideModalTemporarilyAtom } from '@/atoms/settings';
import { systemConfigAtom } from '@/atoms/system';
import { userAtom } from '@/atoms/user';
import { clearAtomStorage, clearStorageUserSettings } from '@/atoms/utils';
import { request } from '@umijs/max';
@@ -31,6 +32,7 @@ export const logout = async (userInfo?: any) => {
clearStorageUserSettings();
clearAtomStorage(userAtom);
clearAtomStorage(hideModalTemporarilyAtom);
clearAtomStorage(systemConfigAtom);
if (res?.logout_url) {
window.location.href = res.logout_url;
+1 -1
View File
@@ -32,7 +32,7 @@ export const rowActionList = [
},
{
key: 'copy',
label: 'common.button.duplicate',
label: 'common.button.clone',
icon: icons.CopyOutlined
},
{
+6 -44
View File
@@ -2,7 +2,6 @@ import { expandKeysAtom } from '@/atoms/clusters';
import DeleteModal from '@/components/delete-modal';
import IconFont from '@/components/icon-font';
import { FilterBar } from '@/components/page-tools';
import { TableOrder } from '@/components/seal-table/types';
import { PageAction } from '@/config';
import { TABLE_SORT_DIRECTIONS } from '@/config/settings';
import useExpandedRowKeys from '@/hooks/use-expanded-row-keys';
@@ -22,13 +21,8 @@ import {
updateProvider
} from './apis';
import AddMaasProvider from './components/add-provider-modal';
import ProviderModels from './components/provider-models';
import { maasProviderOptions } from './config/providers';
import {
FormData,
MaasProviderItem as ListItem,
MaasProviderItem
} from './config/types';
import { FormData, MaasProviderItem as ListItem } from './config/types';
import useCreateProvider from './hooks/use-create-provider';
import useProviderColumns from './hooks/use-provider-columns';
@@ -116,46 +110,14 @@ const MaasProvider: React.FC = () => {
handleDelete({ ...row, name: row.name });
}
if (val === 'copy') {
openProviderModal(PageAction.COPY, 'Copy Provider', row);
openProviderModal(
PageAction.COPY,
intl.formatMessage({ id: 'providers.button.clone' }),
row
);
}
});
const handleOnToggleExpandAll = () => {
// do nothing
};
const handleToggleExpandAll = useMemoizedFn((expanded: boolean) => {
const keys = dataSource.dataList?.map((item) => item.id);
handleExpandAll(expanded, keys);
if (expanded) {
handleOnToggleExpandAll();
}
});
const loadChildrenData = useMemoizedFn(
async (row: MaasProviderItem, options?: any) => {
const params = {
cluster_id: row.id,
page: -1
};
// const data = await queryProviderModels(params, {
// token: options?.token
// });
return [1, 2, 3];
}
);
const handleOnSortChange = (order: TableOrder | Array<TableOrder>) => {
handleTableChange({}, {}, order, { action: 'sort' });
};
const renderChildren = (
list: any,
options: { parent?: any; [key: string]: any }
) => {
return <ProviderModels dataList={list} />;
};
const renderEmpty = (type?: string) => {
if (type !== 'Table') return;
return (
@@ -0,0 +1,50 @@
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Button, Space } from 'antd';
import React from 'react';
export interface WorkerRightActionsProps {
handleDeleteByBatch: () => void;
handleClickPrimary?: () => void;
MonitorButton?: React.ReactNode;
rowSelection: {
selectedRowKeys: React.Key[];
};
}
const WorkerRightActions: React.FC<WorkerRightActionsProps> = ({
handleDeleteByBatch,
handleClickPrimary,
rowSelection,
MonitorButton
}) => {
const intl = useIntl();
return (
<Space size={16}>
{MonitorButton}
<Button
icon={<PlusOutlined />}
type="primary"
onClick={handleClickPrimary}
>
{intl.formatMessage({ id: 'resources.button.create' })}
</Button>
<Button
icon={<DeleteOutlined />}
danger
onClick={handleDeleteByBatch}
disabled={!rowSelection?.selectedRowKeys?.length}
>
<span>
{intl.formatMessage({ id: 'common.button.delete' })}
{rowSelection?.selectedRowKeys?.length > 0 && (
<span>({rowSelection?.selectedRowKeys?.length})</span>
)}
</span>
</Button>
</Space>
);
};
export default WorkerRightActions;
+14 -2
View File
@@ -25,6 +25,7 @@ import useWorkerColumns from '../hooks/use-worker-columns';
import useWorkerMaintenance from '../hooks/use-worker-maintenance';
import UpdateLabels from './update-labels';
import WorkerDetailModal from './worker-detail-modal';
import WorkerRightActions from './worker-right-actions';
const Workers: React.FC<{
clusterId?: string | number | null;
@@ -65,7 +66,10 @@ const Workers: React.FC<{
cluster_id: clusterId
}
});
const { goToGrafana } = useGranfanaLink({ type: 'worker' });
const { goToGrafana, ActionButton } = useGranfanaLink({
type: 'worker',
dataList: dataSource.dataList || []
});
const { MaintenanceModal, handleStopMaintenance, setOpenStatus } =
useWorkerMaintenance({ fetchData: handleSearch });
@@ -268,11 +272,19 @@ const Workers: React.FC<{
handleDeleteByBatch={handleDeleteBatch}
handleSearch={handleSearch}
handleSelectChange={handleClusterChange}
handleClickPrimary={showAddButton ? handleOnAddWorker : undefined}
handleClickPrimary={handleOnAddWorker}
handleInputChange={handleNameChange}
rowSelection={rowSelection}
selectOptions={clusterData.list}
widths={widths}
right={
<WorkerRightActions
handleDeleteByBatch={handleDeleteBatch}
handleClickPrimary={handleOnAddWorker}
rowSelection={rowSelection}
MonitorButton={ActionButton()}
></WorkerRightActions>
}
></FilterBar>
<ConfigProvider renderEmpty={renderEmpty}>
<Table
+1 -2
View File
@@ -223,8 +223,7 @@ const setWorkerIPArg = (params: any) => {
const setImageArgs = (params: any) => {
return `${params.image} \\
--server-url ${params.server} \\
${generateExtraArgs(params)}`;
--server-url ${params.server} \\`;
};
// avaliable for NVIDIA、MThreads
@@ -1,44 +0,0 @@
import { GPUSTACK_API_BASE_URL } from '@/config/settings';
// 1. /grafana/d/gpustack-model/gpustack-model
// 2. /grafana/d/gpustack-worker/gpustack-worker
const useGranfanaLink = (options: {
type: 'model' | 'worker' | 'instance';
}) => {
const goToModelGrafana = (row: { id: number }) => {
window.open(
`/${GPUSTACK_API_BASE_URL}/models/${row.id}/dashboard`,
'_blank'
);
};
const goToWorkerGrafana = (row: { id: number }) => {
window.open(
`/${GPUSTACK_API_BASE_URL}/workers/${row.id}/dashboard`,
'_blank'
);
};
const goToInstanceGrafana = (row: { id: number }) => {
window.open(
`/${GPUSTACK_API_BASE_URL}/model-instances/${row.id}/dashboard`,
'_blank'
);
};
const goToGrafana = (row: { id: number }) => {
if (options.type === 'model') {
goToModelGrafana(row);
} else if (options.type === 'worker') {
goToWorkerGrafana(row);
} else if (options.type === 'instance') {
goToInstanceGrafana(row);
}
};
return {
goToGrafana
};
};
export default useGranfanaLink;
@@ -0,0 +1,81 @@
import { systemConfigAtom } from '@/atoms/system';
import IconFont from '@/components/icon-font';
import { GPUSTACK_API_BASE_URL } from '@/config/settings';
import { useIntl } from '@umijs/max';
import { Button } from 'antd';
import { useAtomValue } from 'jotai';
const useGranfanaLink = (options: {
type: 'model' | 'worker' | 'instance' | 'cluster';
dataList: any[];
}) => {
const intl = useIntl();
const systemConfig = useAtomValue(systemConfigAtom);
const goToModelGrafana = (row: { id: number }) => {
window.open(
`/${GPUSTACK_API_BASE_URL}/models/${row.id}/dashboard`,
'_blank'
);
};
const goToWorkerGrafana = (row: { id: number }) => {
window.open(
`/${GPUSTACK_API_BASE_URL}/workers/${row.id}/dashboard`,
'_blank'
);
};
const goToInstanceGrafana = (row: { id: number }) => {
window.open(
`/${GPUSTACK_API_BASE_URL}/model-instances/${row.id}/dashboard`,
'_blank'
);
};
const goToClusterGrafana = (row: { id: number }) => {
window.open(
`/${GPUSTACK_API_BASE_URL}/clusters/${row.id}/dashboard`,
'_blank'
);
};
const goToGrafana = (row: { id: number }) => {
if (options.type === 'model') {
goToModelGrafana(row);
} else if (options.type === 'worker') {
goToWorkerGrafana(row);
} else if (options.type === 'instance') {
goToInstanceGrafana(row);
} else if (options.type === 'cluster') {
goToClusterGrafana(row);
}
};
const handleClick = () => {
if (options.dataList?.length > 0) {
goToGrafana(options.dataList?.[0]);
}
};
const ActionButton = () => {
if (systemConfig?.showMonitoring) {
return (
<Button
onClick={handleClick}
icon={<IconFont type="icon-metrics" style={{ fontSize: 16 }} />}
>
{intl.formatMessage({ id: 'resources.metrics.details' })}
</Button>
);
}
return null;
};
return {
goToGrafana,
ActionButton
};
};
export default useGranfanaLink;
@@ -1,3 +1,4 @@
import { systemConfigAtom } from '@/atoms/system';
import AutoTooltip from '@/components/auto-tooltip';
import DropdownButtons from '@/components/drop-down-buttons';
import IconFont from '@/components/icon-font';
@@ -18,6 +19,7 @@ import {
import { useIntl } from '@umijs/max';
import { Tooltip } from 'antd';
import { ColumnsType } from 'antd/lib/table';
import { useAtomValue } from 'jotai';
import _ from 'lodash';
import { useMemo } from 'react';
import styled from 'styled-components';
@@ -83,21 +85,6 @@ const ActionList = [
}
];
const setActions = (row: ListItem) => {
return ActionList.filter((action) => {
if (action.key === 'download_ssh_key') {
return !!row.ssh_key_id;
}
if (action.key === 'star_maintenance') {
return !row.maintenance?.enabled;
}
if (action.key === 'stop_maintenance') {
return row.maintenance?.enabled;
}
return true;
});
};
const fieldList = [
{
label: 'resources.table.total',
@@ -252,6 +239,7 @@ const useWorkerColumns = ({
handleSelect: (action: string, record: ListItem) => void;
}): ColumnsType<ListItem> => {
const intl = useIntl();
const systemConfig = useAtomValue(systemConfigAtom);
const renderIP = (text: string, record: ListItem) => {
if (record.advertise_address === record.ip) {
@@ -277,6 +265,25 @@ const useWorkerColumns = ({
return record.ip || record.advertise_address || '';
};
const setActions = (row: ListItem) => {
return ActionList.filter((action) => {
if (action.key === 'download_ssh_key') {
return !!row.ssh_key_id;
}
if (action.key === 'star_maintenance') {
return !row.maintenance?.enabled;
}
if (action.key === 'stop_maintenance') {
return row.maintenance?.enabled;
}
if (action.key === 'metrics') {
return systemConfig.showMonitoring;
}
return true;
});
};
return useMemo<ColumnsType<ListItem>>(
() => [
{
@@ -0,0 +1,22 @@
import { systemConfigAtom } from '@/atoms/system';
import { setAtomStorage } from '@/atoms/utils';
import { SystemConfig } from '@/pages/cluster-management/config/types';
import { request } from '@umijs/max';
export const SYSTEM_CONFIG_API = '/config';
export const fetchSystemConfig = async () => {
try {
const data = await request<SystemConfig>(`${SYSTEM_CONFIG_API}`, {
method: 'GET'
});
setAtomStorage(systemConfigAtom, {
...(data || {}),
showMonitoring:
!!data?.grafana_url || data?.disable_builtin_observability === false
});
} catch (error) {
// handle error if needed
setAtomStorage(systemConfigAtom, {});
}
};
@@ -1,27 +0,0 @@
import { useQueryData } from '@/hooks/use-query-data-list';
import { SystemConfig } from '@/pages/cluster-management/config/types';
import { request } from '@umijs/max';
export const SYSTEM_CONFIG_API = '/config';
const useQuerySystemConfig = () => {
async function querySystemConfig() {
return request<SystemConfig>(`${SYSTEM_CONFIG_API}`, {
method: 'GET'
});
}
const { detailData, loading, cancelRequest, fetchData } =
useQueryData<SystemConfig>({
key: 'system-config',
fetchDetail: querySystemConfig
});
return {
systemConfig: detailData,
loading,
cancelRequest,
fetchSystemConfig: fetchData
};
};
export default useQuerySystemConfig;