From 78368e9979a861a63a1946e44ba1db170986d298 Mon Sep 17 00:00:00 2001 From: jialin Date: Thu, 18 Dec 2025 17:25:15 +0800 Subject: [PATCH] fix: cluster worker config issues --- src/components/logs-viewer/xterm-viewer.tsx | 2 +- src/components/scroller-modal/index.tsx | 25 +++++++++------- .../scroller-modal/use-scroller-context.ts | 11 +++++++ src/hooks/use-chunk-request.ts | 4 +-- src/hooks/use-table-fetch.ts | 2 +- src/hooks/use-watch-list.ts | 2 +- src/locales/en-US/clusters.ts | 4 ++- src/locales/ja-JP/clusters.ts | 7 +++-- src/locales/ru-RU/clusters.ts | 7 +++-- src/locales/zh-CN/clusters.ts | 3 +- src/pages/_components/yaml-editor/index.tsx | 6 ++-- .../cluster-management/cluster-create.tsx | 1 + .../components/add-cluster.tsx | 1 + .../components/add-worker/add-worker-step.tsx | 10 ++++--- .../components/add-worker/index.tsx | 2 +- .../components/cloud-provider-form.tsx | 3 ++ .../components/cluster-form.tsx | 29 ++++++++++++++++--- .../hooks/use-add-worker-message.ts | 5 ++-- .../step-forms/advance-config.tsx | 25 ++++++++++++++-- .../llmodels/components/view-logs-modal.tsx | 4 +-- src/pages/llmodels/index.tsx | 2 +- 21 files changed, 112 insertions(+), 43 deletions(-) create mode 100644 src/components/scroller-modal/use-scroller-context.ts diff --git a/src/components/logs-viewer/xterm-viewer.tsx b/src/components/logs-viewer/xterm-viewer.tsx index ac3cfbd9..cea7e1c4 100644 --- a/src/components/logs-viewer/xterm-viewer.tsx +++ b/src/components/logs-viewer/xterm-viewer.tsx @@ -95,7 +95,7 @@ const LogsViewer: React.FC = forwardRef((props, ref) => { const createChunkConnection = async () => { chunkRequedtRef.current?.current?.cancel?.(); - chunkRequedtRef.current = await setChunkRequest({ + chunkRequedtRef.current = setChunkRequest({ url, params: { ...props.params, diff --git a/src/components/scroller-modal/index.tsx b/src/components/scroller-modal/index.tsx index 09a3bec3..d6dc3061 100644 --- a/src/components/scroller-modal/index.tsx +++ b/src/components/scroller-modal/index.tsx @@ -3,6 +3,7 @@ import useOverlayScroller from '@/hooks/use-overlay-scroller'; import { Modal, type ModalProps } from 'antd'; import React from 'react'; import styled from 'styled-components'; +import { ScrollerContext } from './use-scroller-context'; const Wrapper = styled.div<{ $maxHeight?: number | string }>` max-height: ${({ $maxHeight }) => @@ -16,7 +17,7 @@ const ScrollerModal = ( ) => { const scroller = React.useRef(null); const { saveScrollHeight, restoreScrollHeight } = useBodyScroll(); - const { initialize, destroyInstance } = useOverlayScroller(); + const { initialize, destroyInstance, scrollToBottom } = useOverlayScroller(); React.useEffect(() => { if (props.open) { @@ -68,16 +69,18 @@ const ScrollerModal = ( : {} }} > - + + + ); }; diff --git a/src/components/scroller-modal/use-scroller-context.ts b/src/components/scroller-modal/use-scroller-context.ts new file mode 100644 index 00000000..6e8b6dff --- /dev/null +++ b/src/components/scroller-modal/use-scroller-context.ts @@ -0,0 +1,11 @@ +import { createContext, useContext } from 'react'; + +interface ScrollerContextProps { + scrollToBottom: () => void; +} + +export const ScrollerContext = createContext({ + scrollToBottom: () => {} +}); + +export const useScrollerContext = () => useContext(ScrollerContext); diff --git a/src/hooks/use-chunk-request.ts b/src/hooks/use-chunk-request.ts index 3fa7ea2c..57fab24a 100644 --- a/src/hooks/use-chunk-request.ts +++ b/src/hooks/use-chunk-request.ts @@ -166,11 +166,11 @@ const useSetChunkRequest = () => { return axiosToken.current; }; - const setChunkRequest = async (config: RequestConfig) => { + const setChunkRequest = (config: RequestConfig) => { requestConfig.current = { ...particalConfig, ...config }; retryCount.current = totalCount; clearTimeout(timer.current); - await axiosChunkRequest(requestConfig.current); + axiosChunkRequest(requestConfig.current); return axiosToken; }; diff --git a/src/hooks/use-table-fetch.ts b/src/hooks/use-table-fetch.ts index 9911bc31..6ab059cf 100644 --- a/src/hooks/use-table-fetch.ts +++ b/src/hooks/use-table-fetch.ts @@ -117,7 +117,7 @@ export default function useTableFetch( try { const query = _.omit(params || queryParams, ['page', 'perPage']); - chunkRequedtRef.current = await setChunkRequest({ + chunkRequedtRef.current = setChunkRequest({ url: `${API}?${qs.stringify(_.pickBy(query, (val: any) => !!val))}`, handler: updateHandler }); diff --git a/src/hooks/use-watch-list.ts b/src/hooks/use-watch-list.ts index c57b7a59..dc62e8c3 100644 --- a/src/hooks/use-watch-list.ts +++ b/src/hooks/use-watch-list.ts @@ -30,7 +30,7 @@ export default function useWatchList>(API: string) { const createWatchChunkRequest = useMemoizedFn(async () => { chunkRequestRef.current?.current?.cancel?.(); try { - chunkRequestRef.current = await setChunkRequest({ + chunkRequestRef.current = setChunkRequest({ url: `${watchAPI}`, params: {}, handler: updateWatchDataListHandler diff --git a/src/locales/en-US/clusters.ts b/src/locales/en-US/clusters.ts index b63072cc..9fa5829d 100644 --- a/src/locales/en-US/clusters.ts +++ b/src/locales/en-US/clusters.ts @@ -100,5 +100,7 @@ Same applies to the /opt/dtk directory.`, 'clusters.addworker.dataVolume.tips': 'Specify a data storage path for GPUStack.', 'clusters.table.ip.internal': 'Internal', - 'clusters.table.ip.external': 'External' + 'clusters.table.ip.external': 'External', + 'clusters.form.serverUrl.tips': + 'Specify the server URL accessible from your cloud provider.' }; diff --git a/src/locales/ja-JP/clusters.ts b/src/locales/ja-JP/clusters.ts index 4c71c65c..3b81a430 100644 --- a/src/locales/ja-JP/clusters.ts +++ b/src/locales/ja-JP/clusters.ts @@ -100,7 +100,9 @@ Same applies to the /opt/dtk directory.`, 'clusters.addworker.dataVolume.tips': 'Specify a data storage path for GPUStack.', 'clusters.table.ip.internal': 'Internal', - 'clusters.table.ip.external': 'External' + 'clusters.table.ip.external': 'External', + 'clusters.form.serverUrl.tips': + 'Specify the server URL accessible from your cloud provider.' }; // ========== To-Do: Translate Keys (Remove After Translation) ========== @@ -185,5 +187,6 @@ Same applies to the /opt/dtk directory.`, // 77. 'clusters.addworker.dataVolume': 'GPUStack Data Volume', // 78. 'clusters.addworker.dataVolume.tips': 'Specify a data storage path for GPUStack.', // 79. 'clusters.table.ip.internal': 'Internal', -// 80. 'clusters.table.ip.external': 'External' +// 80. 'clusters.table.ip.external': 'External', +// 81. 'clusters.form.serverUrl.tips': 'Specify the server URL accessible from your cloud provider.' // ========== End of To-Do List ========== diff --git a/src/locales/ru-RU/clusters.ts b/src/locales/ru-RU/clusters.ts index 4e639d73..c66367ff 100644 --- a/src/locales/ru-RU/clusters.ts +++ b/src/locales/ru-RU/clusters.ts @@ -100,7 +100,9 @@ export default { 'clusters.addworker.dataVolume.tips': 'Specify a data storage path for GPUStack.', 'clusters.table.ip.internal': 'Internal', - 'clusters.table.ip.external': 'External' + 'clusters.table.ip.external': 'External', + 'clusters.form.serverUrl.tips': + 'Specify the server URL accessible from your cloud provider.' }; // ========== To-Do: Translate Keys (Remove After Translation) ========== @@ -116,5 +118,6 @@ export default { // 8. 'clusters.addworker.dataVolume': 'GPUStack Data Volume', // 9. 'clusters.addworker.dataVolume.tips': 'Specify a data storage path for GPUStack.', // 10. 'clusters.table.ip.internal': 'Internal', -// 11. 'clusters.table.ip.external': 'External' +// 11. 'clusters.table.ip.external': 'External', +// 12. 'clusters.form.serverUrl.tips': 'Specify the server URL accessible from your cloud provider.' // ================================================================ diff --git a/src/locales/zh-CN/clusters.ts b/src/locales/zh-CN/clusters.ts index 3b4c79c3..5aeffa4d 100644 --- a/src/locales/zh-CN/clusters.ts +++ b/src/locales/zh-CN/clusters.ts @@ -96,5 +96,6 @@ export default { 'clusters.addworker.dataVolume': 'GPUStack 数据卷', 'clusters.addworker.dataVolume.tips': '为 GPUStack 指定数据存储路径。', 'clusters.table.ip.internal': '内', - 'clusters.table.ip.external': '外' + 'clusters.table.ip.external': '外', + 'clusters.form.serverUrl.tips': '指定可从您的云服务提供商访问的服务器地址。' }; diff --git a/src/pages/_components/yaml-editor/index.tsx b/src/pages/_components/yaml-editor/index.tsx index 24ff2aba..047c5d9a 100644 --- a/src/pages/_components/yaml-editor/index.tsx +++ b/src/pages/_components/yaml-editor/index.tsx @@ -18,7 +18,9 @@ const { Text } = Typography; loader.config({ monaco }); -const Container = styled.div` +const Container = styled.div<{ $minHeight: string | number }>` + min-height: ${({ $minHeight }) => + typeof $minHeight === 'number' ? `${$minHeight}px` : $minHeight}; position: relative; border: 1px solid var(--ant-color-border); border-radius: var(--ant-border-radius); @@ -151,7 +153,7 @@ const YamlEditor: React.FC = forwardRef((props, ref) => { }, [value]); return ( - + { return ModuleComponent ? ( = ({ closeIcon={true} maskClosable={false} keyboard={false} + centered={true} width={680} footer={ diff --git a/src/pages/cluster-management/components/add-worker/add-worker-step.tsx b/src/pages/cluster-management/components/add-worker/add-worker-step.tsx index b7bb12e3..4e622a55 100644 --- a/src/pages/cluster-management/components/add-worker/add-worker-step.tsx +++ b/src/pages/cluster-management/components/add-worker/add-worker-step.tsx @@ -30,7 +30,7 @@ const Container = styled.div` * clusterList and onClusterChange are only required when from worker page. */ type AddWorkerProps = { - isModal?: boolean; + actionSource?: 'modal' | 'page'; provider: ProviderType; clusterList?: Global.BaseOption[]; clusterLoading?: boolean; @@ -51,7 +51,7 @@ type AddWorkerProps = { */ const AddWorkerSteps: React.FC = (props) => { const { - isModal = false, + actionSource, registrationInfo, provider, clusterList, @@ -81,10 +81,12 @@ const AddWorkerSteps: React.FC = (props) => { }, [stepList]); React.useEffect(() => { - if (!isModal) { + // this effect is only triggered when used in cluster create page + console.log('actionSource:', actionSource); + if (actionSource === 'page') { createModelsChunkRequest(); } - }, [isModal]); + }, [actionSource]); return ( = (props) => { } > = (props) => { ]} > = forwardRef( ({ action, provider, currentData, credentialList, onFinish }, ref) => { + const { scrollToBottom } = useScrollerContext(); const [form] = Form.useForm(); const intl = useIntl(); const [activeKey, setActiveKey] = React.useState([]); const advanceConfigRef = React.useRef(null); - const handleOnCollapseChange = (keys: string | string[]) => { + const handleOnCollapseChange = async (keys: string | string[]) => { setActiveKey(Array.isArray(keys) ? keys : [keys]); - if (currentData && advanceConfigRef.current) { + if (keys?.includes?.('advanceConfig')) { + await new Promise((resolve) => { + setTimeout(resolve, 500); + }); + scrollToBottom(); } }; + const handleOnFinish = (values: FormData) => { + const workerConfig = yaml2Json(advanceConfigRef.current?.getYamlValue()); + + onFinish({ + ...values, + worker_config: { + ...workerConfig + } + }); + }; + useEffect(() => { if (currentData) { form.setFieldsValue(currentData); + } + }, [currentData]); + useEffect(() => { + if (currentData) { if (advanceConfigRef.current) { const workerConfigYaml = json2Yaml(currentData.worker_config || {}); advanceConfigRef.current?.setYamlValue(workerConfigYaml); @@ -78,7 +99,7 @@ const ClusterForm: React.FC = forwardRef(
= forwardRef( )} name="description" rules={[{ required: false }]}> diff --git a/src/pages/cluster-management/hooks/use-add-worker-message.ts b/src/pages/cluster-management/hooks/use-add-worker-message.ts index 86d7b233..f875245e 100644 --- a/src/pages/cluster-management/hooks/use-add-worker-message.ts +++ b/src/pages/cluster-management/hooks/use-add-worker-message.ts @@ -25,7 +25,6 @@ export default function useAddWorkerMessage() { onCreate: (newItems: any) => { if (startWatchRef.current) { newItemsRef.current = newItemsRef.current.concat(newItems); - console.log('newItemsRef.current:', newItemsRef.current); showAddWorkerMessage(); } } @@ -48,13 +47,13 @@ export default function useAddWorkerMessage() { chunkRequestRef.current?.current?.cancel?.(); resetAddedCount(); try { - chunkRequestRef.current = await setChunkRequest({ + chunkRequestRef.current = setChunkRequest({ url: WORKERS_API, handler: updateHandler }); timerRef.current = setTimeout(() => { startWatchRef.current = true; - }, 1000); + }, 5000); } catch (error) { // ignore } diff --git a/src/pages/cluster-management/step-forms/advance-config.tsx b/src/pages/cluster-management/step-forms/advance-config.tsx index 310546de..27daad18 100644 --- a/src/pages/cluster-management/step-forms/advance-config.tsx +++ b/src/pages/cluster-management/step-forms/advance-config.tsx @@ -1,8 +1,9 @@ +import IconFont from '@/components/icon-font'; import SealInput from '@/components/seal-form/seal-input'; import { PageActionType } from '@/config/types'; import YamlEditor from '@/pages/_components/yaml-editor'; import { useIntl } from '@umijs/max'; -import { Form } from 'antd'; +import { Button, Form } from 'antd'; import React, { forwardRef, useImperativeHandle } from 'react'; import { ProviderType, ProviderValueMap } from '../config'; import { ClusterFormData as FormData } from '../config/types'; @@ -24,7 +25,6 @@ const ClusterAdvanceConfig: React.FC<{ return editorRef.current?.getValue(); }, setYamlValue: (values: any) => { - console.log('setYamlValue:', values, editorRef.current); editorRef.current?.setValue(values); } })); @@ -42,6 +42,9 @@ const ClusterAdvanceConfig: React.FC<{ ]} > + {`${intl.formatMessage({ id: 'clusters.create.workerConfig' })} YAML`} + + + } value={fileContent} height={300} onUpload={(content) => { diff --git a/src/pages/llmodels/components/view-logs-modal.tsx b/src/pages/llmodels/components/view-logs-modal.tsx index e26b99f3..5393ae82 100644 --- a/src/pages/llmodels/components/view-logs-modal.tsx +++ b/src/pages/llmodels/components/view-logs-modal.tsx @@ -72,11 +72,9 @@ const ViewLogsModal: React.FC = (props) => { if (!props.id) return; if (open) { requestRef.current?.current?.cancel?.(); - setChunkRequest({ + requestRef.current = setChunkRequest({ url: `${MODELS_API}/${props.modelId}/instances`, handler: updateHandler - }).then((res) => { - requestRef.current = res; }); } else { logsViewerRef.current?.abort(); diff --git a/src/pages/llmodels/index.tsx b/src/pages/llmodels/index.tsx index 7071aefd..b19c4068 100644 --- a/src/pages/llmodels/index.tsx +++ b/src/pages/llmodels/index.tsx @@ -201,7 +201,7 @@ const Models: React.FC = () => { search: search, categories: categories }; - chunkRequedtRef.current = await setChunkRequest({ + chunkRequedtRef.current = setChunkRequest({ url: `${MODELS_API}?${qs.stringify(_.pickBy(query, (val: any) => !!val))}`, handler: updateHandler });