diff --git a/src/locales/en-US/resources.ts b/src/locales/en-US/resources.ts index 07c62afb..32d91dfe 100644 --- a/src/locales/en-US/resources.ts +++ b/src/locales/en-US/resources.ts @@ -2,6 +2,7 @@ export default { 'resources.title': 'Resources', 'resources.nodes': 'Workers', 'resources.worker': 'Worker', + 'resources.container': 'Container', 'resources.button.create': 'Add Worker', 'resources.button.edit': 'Edit Worker', 'resources.button.edittags': 'Edit Labels', diff --git a/src/locales/ja-JP/resources.ts b/src/locales/ja-JP/resources.ts index b17c9456..b55ea651 100644 --- a/src/locales/ja-JP/resources.ts +++ b/src/locales/ja-JP/resources.ts @@ -1,6 +1,7 @@ export default { 'resources.title': 'リソース', 'resources.nodes': 'ノード', + 'resources.container': 'コンテナ', 'resources.button.create': 'ワーカーを追加', 'resources.button.edit': 'ワーカーを編集', 'resources.button.edittags': 'ラベルを編集', diff --git a/src/locales/ru-RU/resources.ts b/src/locales/ru-RU/resources.ts index e4a949b1..197f332e 100644 --- a/src/locales/ru-RU/resources.ts +++ b/src/locales/ru-RU/resources.ts @@ -1,6 +1,7 @@ export default { 'resources.title': 'Ресурсы', 'resources.nodes': 'Ноды', + 'resources.container': 'Контейнер', 'resources.button.create': 'Добавить воркер', 'resources.button.edit': 'Редактировать воркер', 'resources.button.edittags': 'Редактировать метки', diff --git a/src/locales/tr-TR/resources.ts b/src/locales/tr-TR/resources.ts index b68bcfc7..79f6121b 100644 --- a/src/locales/tr-TR/resources.ts +++ b/src/locales/tr-TR/resources.ts @@ -2,6 +2,7 @@ export default { 'resources.title': 'Kaynaklar', 'resources.nodes': 'İşçi Düğümler', 'resources.worker': 'İşçi Düğüm', + 'resources.container': 'Konteyner', 'resources.button.create': 'İşçi Düğüm Ekle', 'resources.button.edit': 'İşçi Düğümü Düzenle', 'resources.button.edittags': 'Etiketleri Düzenle', diff --git a/src/locales/zh-CN/resources.ts b/src/locales/zh-CN/resources.ts index d7bfce24..ad67a01a 100644 --- a/src/locales/zh-CN/resources.ts +++ b/src/locales/zh-CN/resources.ts @@ -6,6 +6,7 @@ export default { 'resources.button.update': '更新标签', 'resources.nodes': '节点', 'resources.worker': '节点', + 'resources.container': '容器', 'resources.table.hostname': '主机名', 'resources.table.key.tips': '存在相同的 key.', 'resources.form.label': '标签', diff --git a/src/pages/llmodels/components/view-logs-modal.tsx b/src/pages/llmodels/components/view-logs-modal.tsx index 4da69827..0f5adca4 100644 --- a/src/pages/llmodels/components/view-logs-modal.tsx +++ b/src/pages/llmodels/components/view-logs-modal.tsx @@ -2,7 +2,8 @@ import useSetChunkRequest from '@/hooks/use-chunk-request'; import { CloseOutlined, QuestionCircleOutlined } from '@ant-design/icons'; import { BaseSelect, LogsViewer } from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; -import { Button, Checkbox, Modal, Tooltip } from 'antd'; +import { Button, Checkbox, Flex, Modal, Tooltip } from 'antd'; +import dayjs from 'dayjs'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { MODELS_API } from '../apis'; @@ -27,7 +28,9 @@ const ViewLogsModal: React.FC = (props) => { const [isDownloading, setIsDownloading] = useState( status === InstanceStatusMap.Downloading ); - const [selectedWorkerID, setSelectedWorkerID] = useState(null); + const [selectedContainer, setSelectedContainer] = useState( + null + ); const [params, setParams] = useState({ follow: true }); @@ -40,10 +43,17 @@ const ViewLogsModal: React.FC = (props) => { const currentWorkerCountList = useMemo(() => { return ( - countOptions?.find((option) => option.worker_id === selectedWorkerID) + countOptions?.find((option) => option.value === selectedContainer) ?.children || [] ); - }, [countOptions, selectedWorkerID]); + }, [countOptions, selectedContainer]); + + const filteredCountOptions = useMemo(() => { + if (!showPrevious) return countOptions; + return countOptions?.filter((option) => + option.children?.some((child) => child.previous) + ); + }, [countOptions, showPrevious]); const handleCancel = useCallback(() => { logsViewerRef.current?.abort(); @@ -88,7 +98,7 @@ const ViewLogsModal: React.FC = (props) => { const cancelOnClose = () => { logsViewerRef.current?.abort(); requestRef.current?.current?.cancel?.(); - setSelectedWorkerID(null); + setSelectedContainer(null); setShowPrevious(false); cancelRequest(); }; @@ -103,14 +113,15 @@ const ViewLogsModal: React.FC = (props) => { follow: true, watch: !option.previous, previous: option.previous, - worker_id: option.worker_id + worker_id: option.worker_id, + container: option.container }); } - setSelectedWorkerID(option?.worker_id || null); + setSelectedContainer(option?.parentValue || null); }; - const handleWorkerChange = (value: number, option: any) => { - setSelectedWorkerID(value); + const handleContainerChange = (value: string, option: any) => { + setSelectedContainer(value); setShowPrevious(false); const counts = option?.children || []; const lastItem = counts.find((item: any) => !item.previous); @@ -132,8 +143,22 @@ const ViewLogsModal: React.FC = (props) => { handleOnChange(selectItem); }; - const labelRender = (option: any) => { - return {option.label}; + const optionRender = (option: any) => { + const optionData = option?.data || option; + const [container, worker] = optionData.label.split(':'); + const startAt = optionData.start_at + ? dayjs(optionData.start_at).format('YYYY-MM-DD HH:mm:ss') + : ''; + + return ( + + + {container} + [{worker}] + + {startAt && {startAt}} + + ); }; const renderTitle = () => { @@ -189,14 +214,15 @@ const ViewLogsModal: React.FC = (props) => { marginRight: 8 }} > - {intl.formatMessage({ id: 'resources.worker' })} + {intl.formatMessage({ id: 'resources.container' })} } - options={countOptions} - value={selectedWorkerID || undefined} - labelRender={labelRender} - onChange={handleWorkerChange} - style={{ width: 300 }} + options={filteredCountOptions} + value={selectedContainer || undefined} + labelRender={optionRender} + optionRender={optionRender} + onChange={handleContainerChange} + style={{ width: 360 }} > )} diff --git a/src/pages/llmodels/config/types.ts b/src/pages/llmodels/config/types.ts index e5f61f00..707d89fd 100644 --- a/src/pages/llmodels/config/types.ts +++ b/src/pages/llmodels/config/types.ts @@ -384,6 +384,8 @@ export interface InstanceRestartCount { restarts: { previous: boolean; started_at: string; + containers: string[]; }[]; + error?: string | null; }[]; } diff --git a/src/pages/llmodels/services/use-query-instance-restart-count.ts b/src/pages/llmodels/services/use-query-instance-restart-count.ts index 21f91b1d..869302ab 100644 --- a/src/pages/llmodels/services/use-query-instance-restart-count.ts +++ b/src/pages/llmodels/services/use-query-instance-restart-count.ts @@ -6,18 +6,21 @@ import { InstanceRestartCount } from '../config/types'; interface RestartCountOption { label: string; - value: number; + value: string; worker_id: number; name: string; + container: string; + start_at: string; isParent: boolean; + isMain: boolean; children?: { - value: number; + value: string; label: string; start_at: string; previous: boolean; worker_id: number; - isCurrent: boolean; - isPrevious: boolean; + container: string; + parentValue: string; }[]; } @@ -34,32 +37,48 @@ export default function useQueryModelInstanceRestartCount() { const res = await fetchData(instance_id); const list = - res.workers?.map((worker) => { - return { - value: worker.worker_id, - label: worker.name, - worker_id: worker.worker_id, - name: worker.name, - isMain: res.main_worker_id === worker.worker_id, - isParent: true, - children: - worker.restarts?.map((restart, index) => { - return { - value: index, - previous: restart.previous, - label: - index === 0 - ? intl.formatMessage({ id: 'models.instance.currentRun' }) - : intl.formatMessage({ id: 'models.instance.previousRun' }), - start_at: restart.started_at, - worker_id: worker.worker_id, - isCurrent: index === 0, - isPrevious: index === 1 - }; - }) || [] - }; + res.workers?.flatMap((worker) => { + const containerMap = new Map(); + const isMain = res.main_worker_id === worker.worker_id; + + worker.restarts?.forEach((restart, index) => { + restart.containers?.forEach((container) => { + const parentValue = `${worker.worker_id}:${container}`; + const option = containerMap.get(container) || { + value: parentValue, + label: `${container}:${worker.name}`, + worker_id: worker.worker_id, + name: worker.name, + container, + start_at: restart.started_at, + isMain, + isParent: true, + children: [] + }; + + if (!restart.previous) { + option.start_at = restart.started_at; + } + + option.children?.push({ + value: `${parentValue}:${index}`, + previous: restart.previous, + label: restart.previous + ? intl.formatMessage({ id: 'models.instance.previousRun' }) + : intl.formatMessage({ id: 'models.instance.currentRun' }), + start_at: restart.started_at, + worker_id: worker.worker_id, + container, + parentValue + }); + containerMap.set(container, option); + }); + }); + + return Array.from(containerMap.values()); }) || []; + console.log('restart count list: ', list); setDataList(list); return list; };