From 4e8b5e26ae205d13b6a47f030258a6173f1d0e20 Mon Sep 17 00:00:00 2001 From: jialin Date: Wed, 4 Feb 2026 20:05:47 +0800 Subject: [PATCH] feat: monitor button --- src/app.tsx | 2 + src/atoms/system.ts | 12 +++ src/components/icon-font/index.tsx | 2 +- src/locales/en-US/ai.ts | 2 +- src/locales/en-US/models.ts | 2 +- src/locales/en-US/provider.ts | 1 + src/locales/ja-JP/ai.ts | 2 +- src/locales/ja-JP/models.ts | 2 +- src/locales/ja-JP/provider.ts | 1 + src/locales/ru-RU/ai.ts | 2 +- src/locales/ru-RU/models.ts | 2 +- src/locales/ru-RU/provider.ts | 1 + src/locales/zh-CN/ai.ts | 2 +- src/locales/zh-CN/models.ts | 2 +- src/locales/zh-CN/provider.ts | 1 + src/locales/zh-CN/vendor.ts | 2 +- .../cluster-management/cluster-create.tsx | 7 +- src/pages/cluster-management/clusters.tsx | 16 ++++ .../components/right-actions.tsx | 50 ++++++++++++ src/pages/cluster-management/config/index.ts | 5 ++ src/pages/cluster-management/config/types.ts | 7 +- .../hooks/use-cluster-columns.tsx | 27 ++++--- .../llmodels/components/instance-item.tsx | 8 +- src/pages/llmodels/components/table-list.tsx | 7 +- src/pages/llmodels/config/button-actions.ts | 12 +-- src/pages/llmodels/forms/backend.tsx | 2 +- .../llmodels/hooks/use-edit-deployment.ts | 3 +- .../llmodels/hooks/use-models-columns.tsx | 6 ++ src/pages/llmodels/user-models.tsx | 6 +- src/pages/login/apis/index.ts | 2 + src/pages/maas-provider/config/index.ts | 2 +- src/pages/maas-provider/index.tsx | 50 ++---------- .../components/worker-right-actions.tsx | 50 ++++++++++++ src/pages/resources/components/workers.tsx | 16 +++- src/pages/resources/config/gpu-driver.ts | 3 +- src/pages/resources/hooks/use-grafana-link.ts | 44 ---------- .../resources/hooks/use-grafana-link.tsx | 81 +++++++++++++++++++ .../resources/hooks/use-worker-columns.tsx | 37 +++++---- src/services/system/query-system-config.ts | 22 +++++ .../system/use-query-system-config.ts | 27 ------- 40 files changed, 352 insertions(+), 176 deletions(-) create mode 100644 src/atoms/system.ts create mode 100644 src/pages/cluster-management/components/right-actions.tsx create mode 100644 src/pages/resources/components/worker-right-actions.tsx delete mode 100644 src/pages/resources/hooks/use-grafana-link.ts create mode 100644 src/pages/resources/hooks/use-grafana-link.tsx create mode 100644 src/services/system/query-system-config.ts delete mode 100644 src/services/system/use-query-system-config.ts diff --git a/src/app.tsx b/src/app.tsx index c40086d9..c8af9456 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -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) { diff --git a/src/atoms/system.ts b/src/atoms/system.ts new file mode 100644 index 00000000..7e0c19ef --- /dev/null +++ b/src/atoms/system.ts @@ -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({} as SystemConfig); diff --git a/src/components/icon-font/index.tsx b/src/components/icon-font/index.tsx index 5b93627b..1d544043 100644 --- a/src/components/icon-font/index.tsx +++ b/src/components/icon-font/index.tsx @@ -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; diff --git a/src/locales/en-US/ai.ts b/src/locales/en-US/ai.ts index 610733ac..46fb357b 100644 --- a/src/locales/en-US/ai.ts +++ b/src/locales/en-US/ai.ts @@ -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', diff --git a/src/locales/en-US/models.ts b/src/locales/en-US/models.ts index 6fe7b61f..308e015c 100644 --- a/src/locales/en-US/models.ts +++ b/src/locales/en-US/models.ts @@ -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', diff --git a/src/locales/en-US/provider.ts b/src/locales/en-US/provider.ts index 786e2dbd..0748dc24 100644 --- a/src/locales/en-US/provider.ts +++ b/src/locales/en-US/provider.ts @@ -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', diff --git a/src/locales/ja-JP/ai.ts b/src/locales/ja-JP/ai.ts index 610733ac..46fb357b 100644 --- a/src/locales/ja-JP/ai.ts +++ b/src/locales/ja-JP/ai.ts @@ -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', diff --git a/src/locales/ja-JP/models.ts b/src/locales/ja-JP/models.ts index 2840c9d4..dc8d9354 100644 --- a/src/locales/ja-JP/models.ts +++ b/src/locales/ja-JP/models.ts @@ -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': 'ソース', diff --git a/src/locales/ja-JP/provider.ts b/src/locales/ja-JP/provider.ts index 786e2dbd..0748dc24 100644 --- a/src/locales/ja-JP/provider.ts +++ b/src/locales/ja-JP/provider.ts @@ -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', diff --git a/src/locales/ru-RU/ai.ts b/src/locales/ru-RU/ai.ts index 610733ac..46fb357b 100644 --- a/src/locales/ru-RU/ai.ts +++ b/src/locales/ru-RU/ai.ts @@ -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', diff --git a/src/locales/ru-RU/models.ts b/src/locales/ru-RU/models.ts index 9a5e08f8..e7bdcc2a 100644 --- a/src/locales/ru-RU/models.ts +++ b/src/locales/ru-RU/models.ts @@ -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': 'Источник', diff --git a/src/locales/ru-RU/provider.ts b/src/locales/ru-RU/provider.ts index 786e2dbd..0748dc24 100644 --- a/src/locales/ru-RU/provider.ts +++ b/src/locales/ru-RU/provider.ts @@ -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', diff --git a/src/locales/zh-CN/ai.ts b/src/locales/zh-CN/ai.ts index c69d7e86..bfd9322a 100644 --- a/src/locales/zh-CN/ai.ts +++ b/src/locales/zh-CN/ai.ts @@ -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', diff --git a/src/locales/zh-CN/models.ts b/src/locales/zh-CN/models.ts index da5683c0..e1590670 100644 --- a/src/locales/zh-CN/models.ts +++ b/src/locales/zh-CN/models.ts @@ -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': '来源', diff --git a/src/locales/zh-CN/provider.ts b/src/locales/zh-CN/provider.ts index f311ad38..e85c2120 100644 --- a/src/locales/zh-CN/provider.ts +++ b/src/locales/zh-CN/provider.ts @@ -1,6 +1,7 @@ export default { 'providers.title': '提供商', 'providers.button.add': '添加提供商', + 'providers.button.clone': '克隆提供商', 'providers.table.providerName': '提供商', 'providers.table.models': '模型', 'providers.table.proxy': '代理', diff --git a/src/locales/zh-CN/vendor.ts b/src/locales/zh-CN/vendor.ts index 2b0f5bed..cc52e3f3 100644 --- a/src/locales/zh-CN/vendor.ts +++ b/src/locales/zh-CN/vendor.ts @@ -6,5 +6,5 @@ export default { 'vendor.iluvatar': '天数智芯', 'vendor.metax': '沐曦', 'vendor.cambricon': '寒武纪', - 'vendor.thead': '平头哥 (PPU)' + 'vendor.thead': '平头哥 PPU' }; diff --git a/src/pages/cluster-management/cluster-create.tsx b/src/pages/cluster-management/cluster-create.tsx index daa2b6d7..91c56d84 100644 --- a/src/pages/cluster-management/cluster-create.tsx +++ b/src/pages/cluster-management/cluster-create.tsx @@ -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[] @@ -361,7 +362,7 @@ const ClusterCreate: React.FC<{ {renderModules()} diff --git a/src/pages/cluster-management/clusters.tsx b/src/pages/cluster-management/clusters.tsx index 1596598f..ca5448a0 100644 --- a/src/pages/cluster-management/clusters.tsx +++ b/src/pages/cluster-management/clusters.tsx @@ -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={ + + } > void; + handleClickPrimary?: () => void; + MonitorButton?: React.ReactNode; + rowSelection: { + selectedRowKeys: React.Key[]; + }; +} + +const RightActions: React.FC = ({ + handleDeleteByBatch, + handleClickPrimary, + rowSelection, + MonitorButton +}) => { + const intl = useIntl(); + + return ( + + {MonitorButton} + + + + ); +}; + +export default RightActions; diff --git a/src/pages/cluster-management/config/index.ts b/src/pages/cluster-management/config/index.ts index 860db7c6..532cab9c 100644 --- a/src/pages/cluster-management/config/index.ts +++ b/src/pages/cluster-management/config/index.ts @@ -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', diff --git a/src/pages/cluster-management/config/types.ts b/src/pages/cluster-management/config/types.ts index da8def7a..7c752a72 100644 --- a/src/pages/cluster-management/config/types.ts +++ b/src/pages/cluster-management/config/types.ts @@ -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; } diff --git a/src/pages/cluster-management/hooks/use-cluster-columns.tsx b/src/pages/cluster-management/hooks/use-cluster-columns.tsx index 27259376..703b3e6c 100644 --- a/src/pages/cluster-management/hooks/use-cluster-columns.tsx +++ b/src/pages/cluster-management/hooks/use-cluster-columns.tsx @@ -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) => ( <> - {/* onCellClick?.(record, 'name')}> - {record.name} - */} {text} {record.is_default && ( diff --git a/src/pages/llmodels/components/instance-item.tsx b/src/pages/llmodels/components/instance-item.tsx index 5592903d..9ef8448b 100644 --- a/src/pages/llmodels/components/instance-item.tsx +++ b/src/pages/llmodels/components/instance-item.tsx @@ -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 = ({ 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 = ({ 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'); diff --git a/src/pages/llmodels/components/table-list.tsx b/src/pages/llmodels/components/table-list.tsx index a7319b87..300e0a97 100644 --- a/src/pages/llmodels/components/table-list.tsx +++ b/src/pages/llmodels/components/table-list.tsx @@ -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 = ({ 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 = ({ } right={ + {ActionButton()} {page !== 'clusters' && ( { const [showDeprecated, setShowDeprecated] = React.useState(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 || {}) }); } diff --git a/src/pages/llmodels/hooks/use-edit-deployment.ts b/src/pages/llmodels/hooks/use-edit-deployment.ts index 8a368cbb..24274bd5 100644 --- a/src/pages/llmodels/hooks/use-edit-deployment.ts +++ b/src/pages/llmodels/hooks/use-edit-deployment.ts @@ -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' }) }); diff --git a/src/pages/llmodels/hooks/use-models-columns.tsx b/src/pages/llmodels/hooks/use-models-columns.tsx index 05912309..4905cafd 100644 --- a/src/pages/llmodels/hooks/use-models-columns.tsx +++ b/src/pages/llmodels/hooks/use-models-columns.tsx @@ -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; }); diff --git a/src/pages/llmodels/user-models.tsx b/src/pages/llmodels/user-models.tsx index 9772ccd2..489eb5b5 100644 --- a/src/pages/llmodels/user-models.tsx +++ b/src/pages/llmodels/user-models.tsx @@ -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; diff --git a/src/pages/login/apis/index.ts b/src/pages/login/apis/index.ts index 29ca034e..3a7433bf 100644 --- a/src/pages/login/apis/index.ts +++ b/src/pages/login/apis/index.ts @@ -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; diff --git a/src/pages/maas-provider/config/index.ts b/src/pages/maas-provider/config/index.ts index 593a9cdc..d42b75ca 100644 --- a/src/pages/maas-provider/config/index.ts +++ b/src/pages/maas-provider/config/index.ts @@ -32,7 +32,7 @@ export const rowActionList = [ }, { key: 'copy', - label: 'common.button.duplicate', + label: 'common.button.clone', icon: icons.CopyOutlined }, { diff --git a/src/pages/maas-provider/index.tsx b/src/pages/maas-provider/index.tsx index 0da2c7c3..5324600d 100644 --- a/src/pages/maas-provider/index.tsx +++ b/src/pages/maas-provider/index.tsx @@ -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) => { - handleTableChange({}, {}, order, { action: 'sort' }); - }; - - const renderChildren = ( - list: any, - options: { parent?: any; [key: string]: any } - ) => { - return ; - }; - const renderEmpty = (type?: string) => { if (type !== 'Table') return; return ( diff --git a/src/pages/resources/components/worker-right-actions.tsx b/src/pages/resources/components/worker-right-actions.tsx new file mode 100644 index 00000000..2cf5044b --- /dev/null +++ b/src/pages/resources/components/worker-right-actions.tsx @@ -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 = ({ + handleDeleteByBatch, + handleClickPrimary, + rowSelection, + MonitorButton +}) => { + const intl = useIntl(); + + return ( + + {MonitorButton} + + + + ); +}; + +export default WorkerRightActions; diff --git a/src/pages/resources/components/workers.tsx b/src/pages/resources/components/workers.tsx index 33c07524..bc62ac73 100644 --- a/src/pages/resources/components/workers.tsx +++ b/src/pages/resources/components/workers.tsx @@ -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={ + + } > { const setImageArgs = (params: any) => { return `${params.image} \\ - --server-url ${params.server} \\ - ${generateExtraArgs(params)}`; + --server-url ${params.server} \\`; }; // avaliable for NVIDIA、MThreads diff --git a/src/pages/resources/hooks/use-grafana-link.ts b/src/pages/resources/hooks/use-grafana-link.ts deleted file mode 100644 index 1f936172..00000000 --- a/src/pages/resources/hooks/use-grafana-link.ts +++ /dev/null @@ -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; diff --git a/src/pages/resources/hooks/use-grafana-link.tsx b/src/pages/resources/hooks/use-grafana-link.tsx new file mode 100644 index 00000000..2115fd94 --- /dev/null +++ b/src/pages/resources/hooks/use-grafana-link.tsx @@ -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 ( + + ); + } + return null; + }; + + return { + goToGrafana, + ActionButton + }; +}; + +export default useGranfanaLink; diff --git a/src/pages/resources/hooks/use-worker-columns.tsx b/src/pages/resources/hooks/use-worker-columns.tsx index 28068958..c84567ca 100644 --- a/src/pages/resources/hooks/use-worker-columns.tsx +++ b/src/pages/resources/hooks/use-worker-columns.tsx @@ -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 => { 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>( () => [ { diff --git a/src/services/system/query-system-config.ts b/src/services/system/query-system-config.ts new file mode 100644 index 00000000..dd17e357 --- /dev/null +++ b/src/services/system/query-system-config.ts @@ -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(`${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, {}); + } +}; diff --git a/src/services/system/use-query-system-config.ts b/src/services/system/use-query-system-config.ts deleted file mode 100644 index d5f65a16..00000000 --- a/src/services/system/use-query-system-config.ts +++ /dev/null @@ -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(`${SYSTEM_CONFIG_API}`, { - method: 'GET' - }); - } - const { detailData, loading, cancelRequest, fetchData } = - useQueryData({ - key: 'system-config', - fetchDetail: querySystemConfig - }); - - return { - systemConfig: detailData, - loading, - cancelRequest, - fetchSystemConfig: fetchData - }; -}; - -export default useQuerySystemConfig;