chore: display available clusters
This commit is contained in:
@@ -16,3 +16,10 @@ export const getRequestId = () => {
|
|||||||
const store = getDefaultStore();
|
const store = getDefaultStore();
|
||||||
return store.get(requestIdAtom);
|
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.gpuCount': 'GPUs per Replica',
|
||||||
'models.form.gpuType': 'GPU Type',
|
'models.form.gpuType': 'GPU Type',
|
||||||
'models.form.optimizeLongPrompt': 'Optimize Long Prompt',
|
'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.gpuCount': '各レプリカのGPU数',
|
||||||
'models.form.gpuType': 'GPU タイプ',
|
'models.form.gpuType': 'GPU タイプ',
|
||||||
'models.form.optimizeLongPrompt': '長いプロンプトを最適化',
|
'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) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
// 1. 'models.ollama.deprecated.title': 'Deprecation Notice',
|
// 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>.`
|
// 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 ==========
|
// ========== End of To-Do List ==========
|
||||||
|
|||||||
@@ -166,7 +166,10 @@ export default {
|
|||||||
'models.form.gpuCount': 'GPUs per Replica',
|
'models.form.gpuCount': 'GPUs per Replica',
|
||||||
'models.form.gpuType': 'GPU Type',
|
'models.form.gpuType': 'GPU Type',
|
||||||
'models.form.optimizeLongPrompt': 'Optimize Long Prompt',
|
'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) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
@@ -176,5 +179,7 @@ export default {
|
|||||||
// 4. 'models.form.gpuCount': 'GPUs per Replica',
|
// 4. 'models.form.gpuCount': 'GPUs per Replica',
|
||||||
// 5. 'models.form.gpuType': 'GPU Type',
|
// 5. 'models.form.gpuType': 'GPU Type',
|
||||||
// 6. 'models.form.optimizeLongPrompt': 'Optimize Long Prompt',
|
// 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 ==========
|
// ========== End of To-Do List ==========
|
||||||
|
|||||||
@@ -157,5 +157,8 @@ export default {
|
|||||||
'models.form.gpuCount': '每副本 GPU 数量',
|
'models.form.gpuCount': '每副本 GPU 数量',
|
||||||
'models.form.gpuType': 'GPU 类型',
|
'models.form.gpuType': 'GPU 类型',
|
||||||
'models.form.optimizeLongPrompt': '优化长提示',
|
'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 }
|
options: { token: any }
|
||||||
) {
|
) {
|
||||||
return request<{ results: EvaluateResult[] }>(`${MODEL_EVALUATIONS}`, {
|
const result = await request<{ results: EvaluateResult[] }>(
|
||||||
method: 'POST',
|
`${MODEL_EVALUATIONS}`,
|
||||||
data,
|
{
|
||||||
cancelToken: options?.token
|
method: 'POST',
|
||||||
});
|
data,
|
||||||
}
|
cancelToken: options?.token
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// export const evaluationsModelSpec = async (
|
const resultList = result?.results || [];
|
||||||
// data: {
|
|
||||||
// model_specs: EvaluateSpec[];
|
return {
|
||||||
// },
|
results: resultList.map((item) => {
|
||||||
// options: { token: any }
|
return {
|
||||||
// ) => {
|
...item,
|
||||||
// const response = await fetch(`v1/${MODEL_EVALUATIONS}`, {
|
cluster_id: data.model_specs?.[0]?.cluster_id || undefined
|
||||||
// method: 'POST',
|
};
|
||||||
// headers: { 'Content-Type': 'application/json' },
|
})
|
||||||
// body: JSON.stringify(data)
|
};
|
||||||
// });
|
}
|
||||||
// return response.json();
|
|
||||||
// };
|
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
|||||||
getGPUOptionList(params);
|
getGPUOptionList(params);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, []);
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormInnerContext.Provider
|
<FormInnerContext.Provider
|
||||||
@@ -242,7 +242,6 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
|||||||
|
|
||||||
<HuggingFaceForm></HuggingFaceForm>
|
<HuggingFaceForm></HuggingFaceForm>
|
||||||
<LocalPathForm></LocalPathForm>
|
<LocalPathForm></LocalPathForm>
|
||||||
|
|
||||||
<Form.Item<FormData>
|
<Form.Item<FormData>
|
||||||
name="cluster_id"
|
name="cluster_id"
|
||||||
rules={[
|
rules={[
|
||||||
|
|||||||
@@ -486,6 +486,9 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
|||||||
if (open) {
|
if (open) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
fetchSpecData();
|
fetchSpecData();
|
||||||
|
form.current?.getGPUOptionList?.({
|
||||||
|
clusterId: initClusterId()
|
||||||
|
});
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
return () => {
|
return () => {
|
||||||
|
|||||||
@@ -477,6 +477,10 @@ const AddModal: FC<AddModalProps> = (props) => {
|
|||||||
onSelectModelAfterEvaluate={
|
onSelectModelAfterEvaluate={
|
||||||
handleOnSelectModelAfterEvaluate
|
handleOnSelectModelAfterEvaluate
|
||||||
}
|
}
|
||||||
|
clusterId={
|
||||||
|
form.current?.getFieldValue?.('cluster_id') ||
|
||||||
|
initClusterId()
|
||||||
|
}
|
||||||
displayEvaluateStatus={displayEvaluateStatus}
|
displayEvaluateStatus={displayEvaluateStatus}
|
||||||
gpuOptions={[]}
|
gpuOptions={[]}
|
||||||
></SearchModel>
|
></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 {
|
interface SearchInputProps {
|
||||||
hasLinuxWorker?: boolean;
|
hasLinuxWorker?: boolean;
|
||||||
modelSource: string;
|
modelSource: string;
|
||||||
isDownload?: boolean;
|
isDownload?: boolean;
|
||||||
gpuOptions?: any[];
|
gpuOptions?: any[];
|
||||||
|
clusterId: number;
|
||||||
setLoadingModel?: (flag: boolean) => void;
|
setLoadingModel?: (flag: boolean) => void;
|
||||||
onSourceChange?: (source: string) => void;
|
onSourceChange?: (source: string) => void;
|
||||||
onSelectModel: (model: any, manul?: boolean) => void;
|
onSelectModel: (model: any, manul?: boolean) => void;
|
||||||
@@ -70,6 +63,7 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
|
|||||||
isDownload,
|
isDownload,
|
||||||
hasLinuxWorker,
|
hasLinuxWorker,
|
||||||
gpuOptions,
|
gpuOptions,
|
||||||
|
clusterId,
|
||||||
setLoadingModel,
|
setLoadingModel,
|
||||||
onSelectModel,
|
onSelectModel,
|
||||||
onSelectModelAfterEvaluate,
|
onSelectModelAfterEvaluate,
|
||||||
@@ -295,6 +289,7 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...backendObj,
|
...backendObj,
|
||||||
|
cluster_id: clusterId,
|
||||||
source: modelSource,
|
source: modelSource,
|
||||||
...(modelSource === modelSourceMap.huggingface_value
|
...(modelSource === modelSourceMap.huggingface_value
|
||||||
? {
|
? {
|
||||||
|
|||||||
@@ -256,6 +256,7 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open && formData) {
|
if (open && formData) {
|
||||||
setOriginalFormData();
|
setOriginalFormData();
|
||||||
|
getGPUOptionList({ clusterId: formData.cluster_id });
|
||||||
}
|
}
|
||||||
if (!open) {
|
if (!open) {
|
||||||
checkTokenRef.current?.cancel?.();
|
checkTokenRef.current?.cancel?.();
|
||||||
|
|||||||
@@ -192,6 +192,7 @@ export interface CatalogSpec {
|
|||||||
|
|
||||||
export interface EvaluateSpec {
|
export interface EvaluateSpec {
|
||||||
source?: string;
|
source?: string;
|
||||||
|
cluster_id?: number;
|
||||||
huggingface_repo_id?: string;
|
huggingface_repo_id?: string;
|
||||||
huggingface_filename?: string;
|
huggingface_filename?: string;
|
||||||
ollama_library_model_name?: string;
|
ollama_library_model_name?: string;
|
||||||
@@ -231,4 +232,11 @@ export interface EvaluateResult {
|
|||||||
ram: number;
|
ram: number;
|
||||||
vram: 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 { createAxiosToken } from '@/hooks/use-chunk-request';
|
||||||
import { queryModelFilesList } from '@/pages/resources/apis';
|
import { queryModelFilesList } from '@/pages/resources/apis';
|
||||||
import { ListItem as WorkerListItem } from '@/pages/resources/config/types';
|
import { ListItem as WorkerListItem } from '@/pages/resources/config/types';
|
||||||
import { convertFileSize } from '@/utils';
|
import { convertFileSize } from '@/utils';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { useAtomValue } from 'jotai';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { evaluationsModelSpec } from '../apis';
|
import { evaluationsModelSpec } from '../apis';
|
||||||
@@ -148,6 +150,7 @@ export const useCheckCompatibility = () => {
|
|||||||
const requestIdRef = useRef(0);
|
const requestIdRef = useRef(0);
|
||||||
const updateStatusTimer = useRef<any>(null);
|
const updateStatusTimer = useRef<any>(null);
|
||||||
const isLockWarningStatus = useRef<boolean>(false);
|
const isLockWarningStatus = useRef<boolean>(false);
|
||||||
|
const clusterList = useAtomValue(clusterListAtom);
|
||||||
const [warningStatus, setWarningStatus] = useState<MessageStatus>({
|
const [warningStatus, setWarningStatus] = useState<MessageStatus>({
|
||||||
show: false,
|
show: false,
|
||||||
title: '',
|
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 = (
|
const handleCheckCompatibility = (
|
||||||
evaluateResult: EvaluateResult | null
|
evaluateResult: EvaluateResult | null
|
||||||
): MessageStatus => {
|
): MessageStatus => {
|
||||||
console.log('handleCheckCompatibility', evaluateResult);
|
console.log('handleCheckCompatibility', clusterList, evaluateResult);
|
||||||
if (!evaluateResult) {
|
if (!evaluateResult) {
|
||||||
return {
|
return {
|
||||||
show: false,
|
show: false,
|
||||||
@@ -234,7 +252,8 @@ export const useCheckCompatibility = () => {
|
|||||||
compatible,
|
compatible,
|
||||||
compatibility_messages = [],
|
compatibility_messages = [],
|
||||||
scheduling_messages = [],
|
scheduling_messages = [],
|
||||||
resource_claim,
|
resource_claim_by_cluster_id,
|
||||||
|
cluster_id,
|
||||||
error,
|
error,
|
||||||
error_message
|
error_message
|
||||||
} = evaluateResult || {};
|
} = 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;
|
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 = {
|
let msgData = {
|
||||||
title:
|
title:
|
||||||
scheduling_messages?.length > 0
|
scheduling_messages?.length > 0
|
||||||
@@ -260,6 +289,7 @@ export const useCheckCompatibility = () => {
|
|||||||
? scheduling_messages
|
? scheduling_messages
|
||||||
: compatibility_messages?.join(' ')
|
: compatibility_messages?.join(' ')
|
||||||
};
|
};
|
||||||
|
|
||||||
if (hasClaim) {
|
if (hasClaim) {
|
||||||
const ram = convertFileSize(resource_claim.ram, 2);
|
const ram = convertFileSize(resource_claim.ram, 2);
|
||||||
const vram = convertFileSize(resource_claim.vram, 2);
|
const vram = convertFileSize(resource_claim.vram, 2);
|
||||||
@@ -274,11 +304,25 @@ export const useCheckCompatibility = () => {
|
|||||||
title: intl.formatMessage({ id: 'models.form.check.passed' }),
|
title: intl.formatMessage({ id: 'models.form.check.passed' }),
|
||||||
message: intl.formatMessage({ id: messageId }, { ram, vram })
|
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 {
|
return {
|
||||||
show: !compatible || hasClaim,
|
show: !compatible || hasClaim || othersAvailable,
|
||||||
type: !compatible ? 'warning' : 'success',
|
type: !compatible || othersAvailable ? 'warning' : 'success',
|
||||||
...msgData
|
...msgData
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { clusterListAtom } from '@/atoms/models';
|
||||||
import { queryClusterList } from '@/pages/cluster-management/apis';
|
import { queryClusterList } from '@/pages/cluster-management/apis';
|
||||||
import { ClusterListItem } from '@/pages/cluster-management/config/types';
|
import { ClusterListItem } from '@/pages/cluster-management/config/types';
|
||||||
import { queryWorkersList } from '@/pages/resources/apis';
|
import { queryWorkersList } from '@/pages/resources/apis';
|
||||||
@@ -6,6 +7,7 @@ import {
|
|||||||
WorkerStatusMapValue
|
WorkerStatusMapValue
|
||||||
} from '@/pages/resources/config';
|
} from '@/pages/resources/config';
|
||||||
import { ListItem as WorkerListItem } from '@/pages/resources/config/types';
|
import { ListItem as WorkerListItem } from '@/pages/resources/config/types';
|
||||||
|
import { useAtom } from 'jotai';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { queryGPUList } from '../apis';
|
import { queryGPUList } from '../apis';
|
||||||
@@ -188,6 +190,7 @@ export const useGenerateWorkerOptions = () => {
|
|||||||
|
|
||||||
export default function useFormInitialValues() {
|
export default function useFormInitialValues() {
|
||||||
const { getGPUOptionList } = useGenerateGPUOptions();
|
const { getGPUOptionList } = useGenerateGPUOptions();
|
||||||
|
const [, setClusterListAtom] = useAtom(clusterListAtom);
|
||||||
|
|
||||||
const [clusterList, setClusterList] = useState<
|
const [clusterList, setClusterList] = useState<
|
||||||
Global.BaseOption<number, { provider: string; state: string | number }>[]
|
Global.BaseOption<number, { provider: string; state: string | number }>[]
|
||||||
@@ -206,10 +209,12 @@ export default function useFormInitialValues() {
|
|||||||
state: item.state
|
state: item.state
|
||||||
}));
|
}));
|
||||||
setClusterList(list);
|
setClusterList(list);
|
||||||
|
setClusterListAtom(list);
|
||||||
return list;
|
return list;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch cluster list:', error);
|
console.error('Failed to fetch cluster list:', error);
|
||||||
setClusterList([]);
|
setClusterList([]);
|
||||||
|
setClusterListAtom([]);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user