chore: display available clusters
This commit is contained in:
@@ -16,3 +16,10 @@ export const getRequestId = () => {
|
||||
const store = getDefaultStore();
|
||||
return store.get(requestIdAtom);
|
||||
};
|
||||
|
||||
export const clusterListAtom = atom<
|
||||
{
|
||||
label: string;
|
||||
value: number;
|
||||
}[]
|
||||
>([]);
|
||||
|
||||
@@ -167,5 +167,8 @@ export default {
|
||||
'models.form.gpuCount': 'GPUs per Replica',
|
||||
'models.form.gpuType': 'GPU Type',
|
||||
'models.form.optimizeLongPrompt': 'Optimize Long Prompt',
|
||||
'models.form.enableSpeculativeDecoding': 'Enable Speculative Decoding'
|
||||
'models.form.enableSpeculativeDecoding': 'Enable Speculative Decoding',
|
||||
'models.form.check.clusterUnavailable': 'Current cluster is unavailable',
|
||||
'models.form.check.otherClustersAvailable':
|
||||
'Available clusters: {clusters}. Please switch cluster.'
|
||||
};
|
||||
|
||||
@@ -164,11 +164,16 @@ export default {
|
||||
'models.form.gpuCount': '各レプリカのGPU数',
|
||||
'models.form.gpuType': 'GPU タイプ',
|
||||
'models.form.optimizeLongPrompt': '長いプロンプトを最適化',
|
||||
'models.form.enableSpeculativeDecoding': '推測デコーディングを有効にする'
|
||||
'models.form.enableSpeculativeDecoding': '推測デコーディングを有効にする',
|
||||
'models.form.check.clusterUnavailable': 'Current cluster is unavailable',
|
||||
'models.form.check.otherClustersAvailable':
|
||||
'Available clusters: {clusters}. Please switch cluster.'
|
||||
};
|
||||
|
||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||
// 1. 'models.ollama.deprecated.title': 'Deprecation Notice',
|
||||
// 2. 'models.ollama.deprecated.notice': `The Ollama model source has been deprecated as of v0.6.1. For more information, see the <a href="https://github.com/gpustack/gpustack/issues/1979" target="_blank">related GitHub issue</a>.`
|
||||
// 3. 'models.backend.mindie.310p':'Ascend 310P only supports FP16, so you need to set --dtype=float16.'
|
||||
// 3. 'models.backend.mindie.310p':'Ascend 310P only supports FP16, so you need to set --dtype=float16.',
|
||||
// 4. 'models.form.check.clusterUnavailable': 'Current cluster is unavailable',
|
||||
// 5. 'models.form.check.otherClustersAvailable': 'Available clusters: {clusters}. Please switch cluster.'
|
||||
// ========== End of To-Do List ==========
|
||||
|
||||
@@ -166,7 +166,10 @@ export default {
|
||||
'models.form.gpuCount': 'GPUs per Replica',
|
||||
'models.form.gpuType': 'GPU Type',
|
||||
'models.form.optimizeLongPrompt': 'Optimize Long Prompt',
|
||||
'models.form.enableSpeculativeDecoding': 'Enable Speculative Decoding'
|
||||
'models.form.enableSpeculativeDecoding': 'Enable Speculative Decoding',
|
||||
'models.form.check.clusterUnavailable': 'Current cluster is unavailable',
|
||||
'models.form.check.otherClustersAvailable':
|
||||
'Available clusters: {clusters}. Please switch cluster.'
|
||||
};
|
||||
|
||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||
@@ -176,5 +179,7 @@ export default {
|
||||
// 4. 'models.form.gpuCount': 'GPUs per Replica',
|
||||
// 5. 'models.form.gpuType': 'GPU Type',
|
||||
// 6. 'models.form.optimizeLongPrompt': 'Optimize Long Prompt',
|
||||
// 7. 'models.form.enableSpeculativeDecoding': 'Enable Speculative Decoding'
|
||||
// 7. 'models.form.enableSpeculativeDecoding': 'Enable Speculative Decoding',
|
||||
// 8. 'models.form.check.clusterUnavailable': 'Current cluster is unavailable',
|
||||
// 9. 'models.form.check.otherClustersAvailable': 'Available clusters: {clusters}. Please switch cluster.'
|
||||
// ========== End of To-Do List ==========
|
||||
|
||||
@@ -157,5 +157,8 @@ export default {
|
||||
'models.form.gpuCount': '每副本 GPU 数量',
|
||||
'models.form.gpuType': 'GPU 类型',
|
||||
'models.form.optimizeLongPrompt': '优化长提示',
|
||||
'models.form.enableSpeculativeDecoding': '启用推测解码'
|
||||
'models.form.enableSpeculativeDecoding': '启用推测解码',
|
||||
'models.form.check.clusterUnavailable': '当前集群不可用',
|
||||
'models.form.check.otherClustersAvailable':
|
||||
'可用的集群有: {clusters}。请切换集群。'
|
||||
};
|
||||
|
||||
@@ -376,23 +376,23 @@ export async function evaluationsModelSpec(
|
||||
},
|
||||
options: { token: any }
|
||||
) {
|
||||
return request<{ results: EvaluateResult[] }>(`${MODEL_EVALUATIONS}`, {
|
||||
method: 'POST',
|
||||
data,
|
||||
cancelToken: options?.token
|
||||
});
|
||||
}
|
||||
const result = await request<{ results: EvaluateResult[] }>(
|
||||
`${MODEL_EVALUATIONS}`,
|
||||
{
|
||||
method: 'POST',
|
||||
data,
|
||||
cancelToken: options?.token
|
||||
}
|
||||
);
|
||||
|
||||
// export const evaluationsModelSpec = async (
|
||||
// data: {
|
||||
// model_specs: EvaluateSpec[];
|
||||
// },
|
||||
// options: { token: any }
|
||||
// ) => {
|
||||
// const response = await fetch(`v1/${MODEL_EVALUATIONS}`, {
|
||||
// method: 'POST',
|
||||
// headers: { 'Content-Type': 'application/json' },
|
||||
// body: JSON.stringify(data)
|
||||
// });
|
||||
// return response.json();
|
||||
// };
|
||||
const resultList = result?.results || [];
|
||||
|
||||
return {
|
||||
results: resultList.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
cluster_id: data.model_specs?.[0]?.cluster_id || undefined
|
||||
};
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||
getGPUOptionList(params);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
});
|
||||
|
||||
return (
|
||||
<FormInnerContext.Provider
|
||||
@@ -242,7 +242,6 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||
|
||||
<HuggingFaceForm></HuggingFaceForm>
|
||||
<LocalPathForm></LocalPathForm>
|
||||
|
||||
<Form.Item<FormData>
|
||||
name="cluster_id"
|
||||
rules={[
|
||||
|
||||
@@ -486,6 +486,9 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
if (open) {
|
||||
setTimeout(() => {
|
||||
fetchSpecData();
|
||||
form.current?.getGPUOptionList?.({
|
||||
clusterId: initClusterId()
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
return () => {
|
||||
|
||||
@@ -477,6 +477,10 @@ const AddModal: FC<AddModalProps> = (props) => {
|
||||
onSelectModelAfterEvaluate={
|
||||
handleOnSelectModelAfterEvaluate
|
||||
}
|
||||
clusterId={
|
||||
form.current?.getFieldValue?.('cluster_id') ||
|
||||
initClusterId()
|
||||
}
|
||||
displayEvaluateStatus={displayEvaluateStatus}
|
||||
gpuOptions={[]}
|
||||
></SearchModel>
|
||||
|
||||
@@ -40,19 +40,12 @@ const PaginationMain = styled(Pagination)`
|
||||
}
|
||||
`;
|
||||
|
||||
const IconFontWrapper = styled.div`
|
||||
.ant-pagination-item-link {
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
`;
|
||||
|
||||
interface SearchInputProps {
|
||||
hasLinuxWorker?: boolean;
|
||||
modelSource: string;
|
||||
isDownload?: boolean;
|
||||
gpuOptions?: any[];
|
||||
clusterId: number;
|
||||
setLoadingModel?: (flag: boolean) => void;
|
||||
onSourceChange?: (source: string) => void;
|
||||
onSelectModel: (model: any, manul?: boolean) => void;
|
||||
@@ -70,6 +63,7 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
|
||||
isDownload,
|
||||
hasLinuxWorker,
|
||||
gpuOptions,
|
||||
clusterId,
|
||||
setLoadingModel,
|
||||
onSelectModel,
|
||||
onSelectModelAfterEvaluate,
|
||||
@@ -295,6 +289,7 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
|
||||
|
||||
return {
|
||||
...backendObj,
|
||||
cluster_id: clusterId,
|
||||
source: modelSource,
|
||||
...(modelSource === modelSourceMap.huggingface_value
|
||||
? {
|
||||
|
||||
@@ -256,6 +256,7 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
|
||||
useEffect(() => {
|
||||
if (open && formData) {
|
||||
setOriginalFormData();
|
||||
getGPUOptionList({ clusterId: formData.cluster_id });
|
||||
}
|
||||
if (!open) {
|
||||
checkTokenRef.current?.cancel?.();
|
||||
|
||||
@@ -192,6 +192,7 @@ export interface CatalogSpec {
|
||||
|
||||
export interface EvaluateSpec {
|
||||
source?: string;
|
||||
cluster_id?: number;
|
||||
huggingface_repo_id?: string;
|
||||
huggingface_filename?: string;
|
||||
ollama_library_model_name?: string;
|
||||
@@ -231,4 +232,11 @@ export interface EvaluateResult {
|
||||
ram: number;
|
||||
vram: number;
|
||||
};
|
||||
cluster_id?: number;
|
||||
resource_claim_by_cluster_id?: {
|
||||
[key: number]: {
|
||||
ram: number;
|
||||
vram: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { clusterListAtom } from '@/atoms/models';
|
||||
import { createAxiosToken } from '@/hooks/use-chunk-request';
|
||||
import { queryModelFilesList } from '@/pages/resources/apis';
|
||||
import { ListItem as WorkerListItem } from '@/pages/resources/config/types';
|
||||
import { convertFileSize } from '@/utils';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import _ from 'lodash';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { evaluationsModelSpec } from '../apis';
|
||||
@@ -148,6 +150,7 @@ export const useCheckCompatibility = () => {
|
||||
const requestIdRef = useRef(0);
|
||||
const updateStatusTimer = useRef<any>(null);
|
||||
const isLockWarningStatus = useRef<boolean>(false);
|
||||
const clusterList = useAtomValue(clusterListAtom);
|
||||
const [warningStatus, setWarningStatus] = useState<MessageStatus>({
|
||||
show: false,
|
||||
title: '',
|
||||
@@ -220,10 +223,25 @@ export const useCheckCompatibility = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const getAvailableClusters = (ids: string[]) => {
|
||||
const clusterNames: string[] = [];
|
||||
clusterList.forEach?.((item: { value: number; label: string }) => {
|
||||
if (ids.includes(item.value.toString())) {
|
||||
clusterNames.push(item.label);
|
||||
}
|
||||
});
|
||||
return clusterNames.join(', ');
|
||||
};
|
||||
|
||||
const getCurrentCluster = (id: number) => {
|
||||
const cluster = clusterList.find?.((item) => item.value === id);
|
||||
return cluster?.label || '';
|
||||
};
|
||||
|
||||
const handleCheckCompatibility = (
|
||||
evaluateResult: EvaluateResult | null
|
||||
): MessageStatus => {
|
||||
console.log('handleCheckCompatibility', evaluateResult);
|
||||
console.log('handleCheckCompatibility', clusterList, evaluateResult);
|
||||
if (!evaluateResult) {
|
||||
return {
|
||||
show: false,
|
||||
@@ -234,7 +252,8 @@ export const useCheckCompatibility = () => {
|
||||
compatible,
|
||||
compatibility_messages = [],
|
||||
scheduling_messages = [],
|
||||
resource_claim,
|
||||
resource_claim_by_cluster_id,
|
||||
cluster_id,
|
||||
error,
|
||||
error_message
|
||||
} = evaluateResult || {};
|
||||
@@ -248,8 +267,18 @@ export const useCheckCompatibility = () => {
|
||||
};
|
||||
}
|
||||
|
||||
const resourceClaimMap = new Map(
|
||||
Object.entries(resource_claim_by_cluster_id || {})
|
||||
);
|
||||
|
||||
// current cluster resource claim
|
||||
const resource_claim = resourceClaimMap.get(`${cluster_id}`);
|
||||
|
||||
const hasClaim = !!resource_claim?.ram || !!resource_claim?.vram;
|
||||
|
||||
// current cluster is not available, but other clusters are available
|
||||
const othersAvailable = !hasClaim && resourceClaimMap.size > 0;
|
||||
|
||||
let msgData = {
|
||||
title:
|
||||
scheduling_messages?.length > 0
|
||||
@@ -260,6 +289,7 @@ export const useCheckCompatibility = () => {
|
||||
? scheduling_messages
|
||||
: compatibility_messages?.join(' ')
|
||||
};
|
||||
|
||||
if (hasClaim) {
|
||||
const ram = convertFileSize(resource_claim.ram, 2);
|
||||
const vram = convertFileSize(resource_claim.vram, 2);
|
||||
@@ -274,11 +304,25 @@ export const useCheckCompatibility = () => {
|
||||
title: intl.formatMessage({ id: 'models.form.check.passed' }),
|
||||
message: intl.formatMessage({ id: messageId }, { ram, vram })
|
||||
};
|
||||
} else if (othersAvailable) {
|
||||
msgData = {
|
||||
title: intl.formatMessage({
|
||||
id: 'models.form.check.clusterUnavailable'
|
||||
}),
|
||||
message: intl.formatMessage(
|
||||
{
|
||||
id: 'models.form.check.otherClustersAvailable'
|
||||
},
|
||||
{
|
||||
clusters: getAvailableClusters(Array.from(resourceClaimMap.keys()))
|
||||
}
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
show: !compatible || hasClaim,
|
||||
type: !compatible ? 'warning' : 'success',
|
||||
show: !compatible || hasClaim || othersAvailable,
|
||||
type: !compatible || othersAvailable ? 'warning' : 'success',
|
||||
...msgData
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { clusterListAtom } from '@/atoms/models';
|
||||
import { queryClusterList } from '@/pages/cluster-management/apis';
|
||||
import { ClusterListItem } from '@/pages/cluster-management/config/types';
|
||||
import { queryWorkersList } from '@/pages/resources/apis';
|
||||
@@ -6,6 +7,7 @@ import {
|
||||
WorkerStatusMapValue
|
||||
} from '@/pages/resources/config';
|
||||
import { ListItem as WorkerListItem } from '@/pages/resources/config/types';
|
||||
import { useAtom } from 'jotai';
|
||||
import _ from 'lodash';
|
||||
import { useState } from 'react';
|
||||
import { queryGPUList } from '../apis';
|
||||
@@ -188,6 +190,7 @@ export const useGenerateWorkerOptions = () => {
|
||||
|
||||
export default function useFormInitialValues() {
|
||||
const { getGPUOptionList } = useGenerateGPUOptions();
|
||||
const [, setClusterListAtom] = useAtom(clusterListAtom);
|
||||
|
||||
const [clusterList, setClusterList] = useState<
|
||||
Global.BaseOption<number, { provider: string; state: string | number }>[]
|
||||
@@ -206,10 +209,12 @@ export default function useFormInitialValues() {
|
||||
state: item.state
|
||||
}));
|
||||
setClusterList(list);
|
||||
setClusterListAtom(list);
|
||||
return list;
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch cluster list:', error);
|
||||
setClusterList([]);
|
||||
setClusterListAtom([]);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user