diff --git a/src/components/icon-font/icons.ts b/src/components/icon-font/icons.ts index 9f84e967..d1678570 100644 --- a/src/components/icon-font/icons.ts +++ b/src/components/icon-font/icons.ts @@ -64,7 +64,8 @@ const icons = { StarOutlined: React.createElement(StarOutlined), Charger: React.createElement(IconFont, { type: 'icon-charger' }), Disabled: React.createElement(IconFont, { type: 'icon-disabled' }), - CopyOutlined: React.createElement(CopyOutlined) + CopyOutlined: React.createElement(CopyOutlined), + Metrics: React.createElement(IconFont, { type: 'icon-metrics' }) }; export default icons; diff --git a/src/components/icon-font/index.tsx b/src/components/icon-font/index.tsx index fb4f8e9e..5b93627b 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_lqs6kytf9t8.js' + scriptUrl: '//at.alicdn.com/t/c/font_4613488_n2otiiv2h4s.js' }); export default IconFont; diff --git a/src/locales/en-US/resources.ts b/src/locales/en-US/resources.ts index 2eb633e7..26c023b7 100644 --- a/src/locales/en-US/resources.ts +++ b/src/locales/en-US/resources.ts @@ -106,5 +106,6 @@ export default { 'resources.worker.maintenance.tips': 'When enter the maintenance mode, the node will stop scheduling new model deployment tasks. Running instances will not be affected.', 'resources.worker.noCluster.tips': - 'No available clusters. Please create a cluster before adding a node.' + 'No available clusters. Please create a cluster before adding a node.', + 'resources.metrics.details': 'Metrics Details' }; diff --git a/src/locales/ja-JP/resources.ts b/src/locales/ja-JP/resources.ts index 3020e30c..5a9b3b52 100644 --- a/src/locales/ja-JP/resources.ts +++ b/src/locales/ja-JP/resources.ts @@ -107,7 +107,8 @@ export default { 'resources.worker.maintenance.tips': 'When maintenance mode is enabled, the node will stop scheduling new model deployment tasks. Running instances will not be affected.', 'resources.worker.noCluster.tips': - 'No available clusters. Please create a cluster before adding a node.' + 'No available clusters. Please create a cluster before adding a node.', + 'resources.metrics.details': 'Metrics Details' }; // ========== To-Do: Translate Keys (Remove After Translation) ========== diff --git a/src/locales/ru-RU/resources.ts b/src/locales/ru-RU/resources.ts index 2a10de85..ff8f2294 100644 --- a/src/locales/ru-RU/resources.ts +++ b/src/locales/ru-RU/resources.ts @@ -105,7 +105,8 @@ export default { 'resources.worker.maintenance.tips': 'When maintenance mode is enabled, the node will stop scheduling new model deployment tasks. Running instances will not be affected.', 'resources.worker.noCluster.tips': - 'No available clusters. Please create a cluster before adding a node.' + 'No available clusters. Please create a cluster before adding a node.', + 'resources.metrics.details': 'Metrics Details' }; // ========== To-Do: Translate Keys (Remove After Translation) ========== @@ -115,5 +116,6 @@ export default { // 5. 'resources.worker.maintenance.remark': 'Remark', // 6. 'resources.worker.maintenance.remark.rules': 'Please enter maintenance remarks', // 7. 'resources.worker.maintenance.tips': 'When maintenance mode is enabled, the node will stop scheduling new model deployment tasks. Running instances will not be affected.', -// 8. 'resources.worker.noCluster.tips': 'No available clusters. Please create a cluster before adding a node.' +// 8. 'resources.worker.noCluster.tips': 'No available clusters. Please create a cluster before adding a node.', +// 9. 'resources.metrics.details': 'Metrics Details' // ========== End of To-Do List ========== diff --git a/src/locales/zh-CN/resources.ts b/src/locales/zh-CN/resources.ts index 59425b90..44a0469a 100644 --- a/src/locales/zh-CN/resources.ts +++ b/src/locales/zh-CN/resources.ts @@ -102,5 +102,6 @@ export default { 'resources.worker.maintenance.remark.rules': '请输入维护备注', 'resources.worker.maintenance.tips': '进入维护模式后,节点将停止调度新的模型实例部署任务,正在运行的实例不会受到影响。', - 'resources.worker.noCluster.tips': '当前无可用集群,请先创建集群再添加节点。' + 'resources.worker.noCluster.tips': '当前无可用集群,请先创建集群再添加节点。', + 'resources.metrics.details': '监控详情' }; diff --git a/src/pages/llmodels/components/instance-item.tsx b/src/pages/llmodels/components/instance-item.tsx index 15fdc65b..5592903d 100644 --- a/src/pages/llmodels/components/instance-item.tsx +++ b/src/pages/llmodels/components/instance-item.tsx @@ -11,6 +11,7 @@ import { HandlerOptions } from '@/hooks/use-chunk-fetch'; import useDownloadStream from '@/hooks/use-download-stream'; import { useBenchmarkTargetInstance } from '@/pages/llmodels/hooks/use-run-benchmark'; import { ListItem as WorkerListItem } from '@/pages/resources/config/types'; +import useGranfanaLink from '@/pages/resources/hooks/use-grafana-link'; import { convertFileSize } from '@/utils'; import { DeleteOutlined, @@ -327,6 +328,12 @@ const childActionList = [ ], icon: }, + { + label: 'resources.metrics.details', + key: 'metrics', + status: [InstanceStatusMap.Running], + icon: + }, { label: 'common.button.downloadLog', key: 'download', @@ -428,6 +435,7 @@ const InstanceItem: React.FC = ({ defaultOpenId, handleChildSelect }) => { + const { goToGrafana } = useGranfanaLink({ type: 'instance' }); const { runBenchmarkOnInstance } = useBenchmarkTargetInstance(); const [api, contextHolder] = notification.useNotification({ stack: { threshold: 1 } @@ -689,6 +697,8 @@ const InstanceItem: React.FC = ({ filename: createFileName(instanceData.name), downloadNotification }); + } else if (val === 'metrics') { + goToGrafana(instanceData); } else { handleChildSelect(val, instanceData); } diff --git a/src/pages/llmodels/components/table-list.tsx b/src/pages/llmodels/components/table-list.tsx index cab036dc..a7319b87 100644 --- a/src/pages/llmodels/components/table-list.tsx +++ b/src/pages/llmodels/components/table-list.tsx @@ -9,7 +9,6 @@ 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'; @@ -18,6 +17,7 @@ import useNoResourceResult from '@/pages/llmodels/hooks/use-no-resource-result'; import { TargetStatusValueMap } from '@/pages/model-routes/config'; import useOpenPlayground from '@/pages/model-routes/hooks/use-open-playground'; import useQueryTargets from '@/pages/model-routes/services/use-query-targets'; +import useGranfanaLink from '@/pages/resources/hooks/use-grafana-link'; import { handleBatchRequest } from '@/utils'; import { DownOutlined, SearchOutlined, SyncOutlined } from '@ant-design/icons'; import { useIntl, useNavigate, useSearchParams } from '@umijs/max'; @@ -170,6 +170,9 @@ const Models: React.FC = ({ useEffect(() => { fetchTargets({}); }, []); + const { goToGrafana } = useGranfanaLink({ + type: 'model' + }); const [openLogModal, setOpenLogModal] = useState(false); const [openDeployModal, setOpenDeployModal] = useState<{ @@ -451,6 +454,9 @@ const Models: React.FC = ({ name: targetRoute?.route_name || '' }); } + if (val === 'metrics') { + goToGrafana(row); + } } catch (error) { // ignore } diff --git a/src/pages/llmodels/config/button-actions.ts b/src/pages/llmodels/config/button-actions.ts index c3685c45..ba4a170e 100644 --- a/src/pages/llmodels/config/button-actions.ts +++ b/src/pages/llmodels/config/button-actions.ts @@ -39,6 +39,11 @@ export const ActionList: ActionItem[] = [ key: 'edit', icon: icons.EditOutlined }, + { + label: 'resources.metrics.details', + key: 'metrics', + icon: icons.Metrics + }, { label: 'models.openinplayground', key: 'chat', diff --git a/src/pages/llmodels/index.tsx b/src/pages/llmodels/index.tsx index 4f3e5a9b..aaedc70a 100644 --- a/src/pages/llmodels/index.tsx +++ b/src/pages/llmodels/index.tsx @@ -35,7 +35,6 @@ const Models: React.FC<{ clusterId?: number }> = ({ clusterId }) => { loadend: false, total: 0 }); - const chunkRequedtRef = useRef(); const chunkInstanceRequedtRef = useRef(); const isPageHidden = useRef(false); diff --git a/src/pages/resources/components/workers.tsx b/src/pages/resources/components/workers.tsx index 0d0d1b5e..33c07524 100644 --- a/src/pages/resources/components/workers.tsx +++ b/src/pages/resources/components/workers.tsx @@ -8,6 +8,7 @@ import { DockerStepsFromWorker } from '@/pages/cluster-management/components/add import { ClusterListItem } from '@/pages/cluster-management/config/types'; import useAddWorker from '@/pages/cluster-management/hooks/use-add-worker'; import useNoResourceResult from '@/pages/llmodels/hooks/use-no-resource-result'; +import useGranfanaLink from '@/pages/resources/hooks/use-grafana-link'; import { useIntl } from '@umijs/max'; import { useMemoizedFn } from 'ahooks'; import { ConfigProvider, Table, message } from 'antd'; @@ -20,7 +21,6 @@ import { updateWorker } from '../apis'; import { ListItem } from '../config/types'; -import useTerminalTabs from '../hooks/use-terminal-tabs'; import useWorkerColumns from '../hooks/use-worker-columns'; import useWorkerMaintenance from '../hooks/use-worker-maintenance'; import UpdateLabels from './update-labels'; @@ -65,7 +65,7 @@ const Workers: React.FC<{ cluster_id: clusterId } }); - const { TerminalPanel, terminals, handleAddTerminal } = useTerminalTabs(); + const { goToGrafana } = useGranfanaLink({ type: 'worker' }); const { MaintenanceModal, handleStopMaintenance, setOpenStatus } = useWorkerMaintenance({ fetchData: handleSearch }); @@ -199,9 +199,8 @@ const Workers: React.FC<{ if (val === 'stop_maintenance') { handleStopMaintenance(record); } - - if (val === 'terminal') { - handleAddTerminal(record); + if (val === 'metrics') { + goToGrafana(record); } }); diff --git a/src/pages/resources/hooks/use-grafana-link.ts b/src/pages/resources/hooks/use-grafana-link.ts new file mode 100644 index 00000000..21eacf70 --- /dev/null +++ b/src/pages/resources/hooks/use-grafana-link.ts @@ -0,0 +1,42 @@ +import { GPUSTACK_API_BASE_URL } from '@/config/settings'; + +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-worker-columns.tsx b/src/pages/resources/hooks/use-worker-columns.tsx index 32b43745..28068958 100644 --- a/src/pages/resources/hooks/use-worker-columns.tsx +++ b/src/pages/resources/hooks/use-worker-columns.tsx @@ -1,5 +1,6 @@ import AutoTooltip from '@/components/auto-tooltip'; import DropdownButtons from '@/components/drop-down-buttons'; +import IconFont from '@/components/icon-font'; import LabelsCell from '@/components/label-cell'; import ProgressBar from '@/components/progress-bar'; import InfoColumn from '@/components/simple-table/info-column'; @@ -42,6 +43,11 @@ const IPWrapper = styled.span` const ActionList = [ { label: 'common.button.edit', key: 'edit', icon: }, + { + label: 'resources.metrics.details', + key: 'metrics', + icon: + }, // { // label: 'common.button.detail', // key: 'details',