feat: monitor button
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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,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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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,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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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,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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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,6 +1,7 @@
|
||||
export default {
|
||||
'providers.title': '提供商',
|
||||
'providers.button.add': '添加提供商',
|
||||
'providers.button.clone': '克隆提供商',
|
||||
'providers.table.providerName': '提供商',
|
||||
'providers.table.models': '模型',
|
||||
'providers.table.proxy': '代理',
|
||||
|
||||
@@ -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()}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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={{
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -32,7 +32,7 @@ export const rowActionList = [
|
||||
},
|
||||
{
|
||||
key: 'copy',
|
||||
label: 'common.button.duplicate',
|
||||
label: 'common.button.clone',
|
||||
icon: icons.CopyOutlined
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user