diff --git a/src/hooks/use-update-chunk-list.ts b/src/hooks/use-update-chunk-list.ts
index 10808cf6..e30127f8 100644
--- a/src/hooks/use-update-chunk-list.ts
+++ b/src/hooks/use-update-chunk-list.ts
@@ -15,7 +15,10 @@ export function useUpdateChunkedList(options: {
events?: EventsType[];
dataList?: any[];
limit?: number;
- setDataList: (args: any, opts?: any) => void;
+ onCreate?: (args: any) => void;
+ onUpdate?: (args: any) => void;
+ onDelete?: (args: any) => void;
+ setDataList?: (args: any, opts?: any) => void;
callback?: (args: any) => void;
filterFun?: (args: any) => boolean;
mapFun?: (args: any) => any;
@@ -56,6 +59,7 @@ export function useUpdateChunkedList(options: {
collections = data?.collection?.map(options?.mapFun);
}
const ids: any[] = data?.ids || [];
+
// CREATE
if (data?.type === WatchEventType.CREATE && events.includes('CREATE')) {
const newDataList = collections.reduce((acc: any[], item: any) => {
@@ -71,11 +75,15 @@ export function useUpdateChunkedList(options: {
return acc;
}, []);
+
cacheDataListRef.current = [
...newDataList,
...cacheDataListRef.current
].slice(0, limit);
+
+ options.onCreate?.(newDataList);
}
+
// DELETE
if (data?.type === WatchEventType.DELETE && events.includes('DELETE')) {
cacheDataListRef.current = cacheDataListRef.current?.filter(
@@ -88,6 +96,7 @@ export function useUpdateChunkedList(options: {
deletedIds: [...deletedIdsRef.current]
});
}
+
// UPDATE
if (data?.type === WatchEventType.UPDATE && events.includes('UPDATE')) {
collections?.forEach((item: any) => {
@@ -102,6 +111,7 @@ export function useUpdateChunkedList(options: {
updateItem,
...cacheDataListRef.current.slice(0, limit - 1)
];
+ options.onCreate?.([updateItem]);
}
});
}
diff --git a/src/locales/en-US/clusters.ts b/src/locales/en-US/clusters.ts
index 95c7184d..1f43158f 100644
--- a/src/locales/en-US/clusters.ts
+++ b/src/locales/en-US/clusters.ts
@@ -88,5 +88,7 @@ Same applies to the /opt/dtk directory.`,
'clusters.addworker.vendorNotes.title': 'Notes for {vendor} Device',
'clusters.button.genToken':
'Need to create a new token? Click here.',
- 'clusters.addworker.amdNotes-01': `If the /opt/rocm directory does not exist, please create a symbolic link pointing to the ROCm installed path: ln -s /path/to/rocm /opt/rocm.`
+ 'clusters.addworker.amdNotes-01': `If the /opt/rocm directory does not exist, please create a symbolic link pointing to the ROCm installed path: ln -s /path/to/rocm /opt/rocm.`,
+ 'clusters.addworker.message.success':
+ '{count} workers have been added to the cluster.'
};
diff --git a/src/locales/ja-JP/clusters.ts b/src/locales/ja-JP/clusters.ts
index 540396b7..0104b161 100644
--- a/src/locales/ja-JP/clusters.ts
+++ b/src/locales/ja-JP/clusters.ts
@@ -88,7 +88,9 @@ Same applies to the /opt/dtk directory.`,
'clusters.addworker.vendorNotes.title': 'Notes for {vendor} Device',
'clusters.button.genToken':
'Need to create a new token? Click here.',
- 'clusters.addworker.amdNotes-01': `If the /opt/rocm directory does not exist, please create a symbolic link pointing to the ROCm installed path: ln -s /path/to/rocm /opt/rocm.`
+ 'clusters.addworker.amdNotes-01': `If the /opt/rocm directory does not exist, please create a symbolic link pointing to the ROCm installed path: ln -s /path/to/rocm /opt/rocm.`,
+ 'clusters.addworker.message.success':
+ '{count} workers have been added to the cluster.'
};
// ========== To-Do: Translate Keys (Remove After Translation) ==========
@@ -165,4 +167,5 @@ Same applies to the /opt/dtk directory.`,
// 71. 'clusters.addworker.cacheVolume': 'Model Cache Volume Mount',
// 72. 'clusters.addworker.cacheVolume.tips': 'If you want to customize the model cache directory, you can specify the path to mount it.',
// 73. 'clusters.addworker.cacheVolume.holder': 'e.g. /data/cache (path must start with /)',
+// 74. 'clusters.addworker.message.success': '{count} workers have been added to the cluster.'
// ========== End of To-Do List ==========
diff --git a/src/locales/ru-RU/clusters.ts b/src/locales/ru-RU/clusters.ts
index f36868cd..46ff3f38 100644
--- a/src/locales/ru-RU/clusters.ts
+++ b/src/locales/ru-RU/clusters.ts
@@ -88,7 +88,9 @@ export default {
'clusters.addworker.vendorNotes.title': 'Примечания для устройств {vendor}',
'clusters.button.genToken':
'Need to create a new token? Click here.',
- 'clusters.addworker.amdNotes-01': `If the /opt/rocm directory does not exist, please create a symbolic link pointing to the ROCm installed path: ln -s /path/to/rocm /opt/rocm.`
+ 'clusters.addworker.amdNotes-01': `If the /opt/rocm directory does not exist, please create a symbolic link pointing to the ROCm installed path: ln -s /path/to/rocm /opt/rocm.`,
+ 'clusters.addworker.message.success':
+ '{count} workers have been added to the cluster.'
};
// ========== To-Do: Translate Keys (Remove After Translation) ==========
@@ -96,4 +98,5 @@ export default {
// 2. 'clusters.button.genToken': 'Need to create a new token? Click here.',
// 3. 'clusters.addworker.cacheVolume': 'Model Cache Volume Mount',
// 4. 'clusters.addworker.cacheVolume.tips': 'If you want to customize the model cache directory, you can specify the path to mount it.',
+// 5. 'clusters.addworker.message.success': '{count} workers have been added to the cluster.'
// ================================================================
diff --git a/src/locales/zh-CN/clusters.ts b/src/locales/zh-CN/clusters.ts
index d360a947..5f6d220c 100644
--- a/src/locales/zh-CN/clusters.ts
+++ b/src/locales/zh-CN/clusters.ts
@@ -87,5 +87,6 @@ export default {
'clusters.button.genToken':
'需要创建令牌?点击这里。',
'clusters.addworker.amdNotes-01':
- '如果 /opt/rocm 目录不存在,请创建一个指向已安装 ROCm 路径的符号链接:ln -s /path/to/rocm /opt/rocm。'
+ '如果 /opt/rocm 目录不存在,请创建一个指向已安装 ROCm 路径的符号链接:ln -s /path/to/rocm /opt/rocm。',
+ 'clusters.addworker.message.success': '已将 {count} 个工作节点添加到集群中。'
};
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 792eb704..4fd23891 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
@@ -1,4 +1,5 @@
import AlertInfoBlock from '@/components/alert-info/block';
+import useAddWorkerMessage from '@/pages/cluster-management/hooks/use-add-worker-message';
import { ExclamationCircleFilled } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import React from 'react';
@@ -56,14 +57,21 @@ const AddWorkerSteps: React.FC = (props) => {
onClusterChange
} = props || {};
const intl = useIntl();
- const { update, summary, register } = useSummaryStatus();
const [collapseKey, setCollapseKey] = React.useState>(
new Set([stepList[0]])
);
+ const startWatchRef = React.useRef(false);
+ const { update, summary, register } = useSummaryStatus();
+ const { contextHolder, createModelsChunkRequest } = useAddWorkerMessage({
+ startWatch: startWatchRef
+ });
const onToggle = (open: boolean, key: string) => {
setCollapseKey(open ? new Set([key]) : new Set());
+ if (key === StepNamesMap.RunCommand && open) {
+ startWatchRef.current = true;
+ }
};
const handleOnClusterChange = (value: number, row?: any) => {
@@ -75,6 +83,10 @@ const AddWorkerSteps: React.FC = (props) => {
setCollapseKey(new Set([stepList[0]]));
}, [stepList]);
+ React.useEffect(() => {
+ createModelsChunkRequest();
+ }, []);
+
return (
= (props) => {
>
)}
+ {contextHolder}
);
};
diff --git a/src/pages/cluster-management/hooks/use-add-worker-message.ts b/src/pages/cluster-management/hooks/use-add-worker-message.ts
new file mode 100644
index 00000000..c6e9e6b9
--- /dev/null
+++ b/src/pages/cluster-management/hooks/use-add-worker-message.ts
@@ -0,0 +1,64 @@
+import useSetChunkRequest from '@/hooks/use-chunk-request';
+import useUpdateChunkedList from '@/hooks/use-update-chunk-list';
+import { useIntl } from '@umijs/max';
+import { message } from 'antd';
+import _ from 'lodash';
+import { useRef } from 'react';
+import { WORKERS_API } from '../../resources/apis';
+
+export default function useAddWorkerMessage(params: {
+ startWatch?: React.RefObject;
+}) {
+ const chunkRequestRef = useRef(null);
+ const newItemsRef = useRef([]);
+ const intl = useIntl();
+ const [messageApi, contextHolder] = message.useMessage();
+
+ const showAddWorkerMessage = _.debounce(() => {
+ if (newItemsRef.current.length > 0) {
+ const count = newItemsRef.current.length;
+ messageApi.success(
+ intl.formatMessage(
+ { id: 'clusters.addworker.message.success' },
+ { count }
+ )
+ );
+ newItemsRef.current = [];
+ }
+ }, 300);
+
+ const { setChunkRequest } = useSetChunkRequest();
+ const { updateChunkedList } = useUpdateChunkedList({
+ events: ['CREATE', 'INSERT'],
+ dataList: [],
+ onCreate: (newItems: any) => {
+ if (params.startWatch?.current) {
+ newItemsRef.current = newItemsRef.current.concat(newItems);
+ showAddWorkerMessage();
+ }
+ }
+ });
+
+ const updateHandler = (list: any) => {
+ _.each(list, (data: any) => {
+ updateChunkedList(data);
+ });
+ };
+
+ const createModelsChunkRequest = async () => {
+ chunkRequestRef.current?.current?.cancel?.();
+ try {
+ chunkRequestRef.current = setChunkRequest({
+ url: WORKERS_API,
+ handler: updateHandler
+ });
+ } catch (error) {
+ // ignore
+ }
+ };
+
+ return {
+ contextHolder,
+ createModelsChunkRequest
+ };
+}
diff --git a/src/pages/resources/components/workers.tsx b/src/pages/resources/components/workers.tsx
index bd82382b..7a349bf9 100644
--- a/src/pages/resources/components/workers.tsx
+++ b/src/pages/resources/components/workers.tsx
@@ -48,6 +48,7 @@ const Workers: React.FC = () => {
} = useTableFetch({
fetchAPI: queryWorkersList,
deleteAPI: deleteWorker,
+ events: ['UPDATE', 'DELETE', 'INSERT'],
contentForDelete: 'resources.worker',
watch: true,
API: WORKERS_API