chore: restart on error

This commit is contained in:
jialin
2025-03-31 16:21:40 +08:00
parent f83e49bd3d
commit eff88528d3
20 changed files with 162 additions and 105 deletions
+6
View File
@@ -894,3 +894,9 @@ body {
font-weight: var(--font-weight-bold);
padding: 2px;
}
.note-text {
font-size: var(--font-size-base);
color: var(--ant-color-text-tertiary);
font-weight: var(--font-weight-medium);
}
+4 -1
View File
@@ -124,5 +124,8 @@ export default {
'models.table.status': 'Status',
'models.form.submit.anyway': 'Submit Anyway',
'models.form.evaluating': 'Evaluating...',
'models.form.incompatible': 'Incompatible'
'models.form.incompatible': 'Incompatible',
'models.form.restart.onerror': 'Auto-Restart On Error',
'models.form.restart.onerror.tips':
'When an error occurs, it will automatically attempt to restart.'
};
+3 -2
View File
@@ -41,10 +41,11 @@ export default {
'resources.table.wokers': 'workers',
'resources.worker.linuxormaxos': 'Linux or MacOS',
'resources.table.unified': 'Unified Memory',
'resources.worker.add.step1': 'Get Token',
'resources.worker.add.step1':
'Get Token <span class="note-text">(Run on the server)</span>',
'resources.worker.add.step2': 'Register Worker',
'resources.worker.add.step2.tips':
'Note: <span style="color: #000;font-weight: 600">mytoken</span> is the token obtained in the first step.',
'Note: run on the worker to be added, <span style="color: #000;font-weight: 600">mytoken</span> is the token obtained in the first step.',
'resources.worker.add.step3':
'After success, refresh the workers list to see the new worker.',
'resources.worker.container.supported': 'Do not support MacOS or Windows.',
+6 -1
View File
@@ -121,7 +121,10 @@ export default {
'models.table.status': 'Status',
'models.form.submit.anyway': 'Submit Anyway',
'models.form.evaluating': 'Evaluating...',
'models.form.incompatible': 'Incompatible'
'models.form.incompatible': 'Incompatible',
'models.form.restart.onerror': 'Auto-Restart On Error',
'models.form.restart.onerror.tips':
'When an error occurs, it will automatically attempt to restart.'
};
// ========== To-Do: Translate Keys (Remove After Translation) ==========
@@ -130,4 +133,6 @@ export default {
// 3. 'models.form.submit.anyway',
// 4. 'models.form.evaluating',
// 5. 'models.form.incompatible',
// 6. 'models.form.restart.onerror',
// 7. 'models.form.restart.onerror.tips'
// ========== End of To-Do List ==========
+6 -3
View File
@@ -41,10 +41,11 @@ export default {
'resources.table.wokers': 'ワーカー',
'resources.worker.linuxormaxos': 'LinuxまたはMacOS',
'resources.table.unified': '統合メモリ',
'resources.worker.add.step1': 'トークンを取得',
'resources.worker.add.step1':
'Get Token <span class="note-text">(Run on the server)</span>',
'resources.worker.add.step2': 'ワーカーを登録',
'resources.worker.add.step2.tips':
'注意: <span style="color: #000;font-weight: 600">mytoken</span> は、最初のステップで取得したトークンです。',
'Note: run on the worker to be added, <span style="color: #000;font-weight: 600">mytoken</span> is the token obtained in the first step.',
'resources.worker.add.step3':
'成功後、ワーカーリストを更新して新しいワーカーを確認してください。',
'resources.worker.container.supported':
@@ -85,5 +86,7 @@ export default {
// 9. 'resources.modelfiles.storagePath.holder',
// 10. 'resources.filter.worker',
// 11. 'resources.filter.source',
// 12. 'resources.modelfiles.delete.tips'
// 12. 'resources.modelfiles.delete.tips',
// 13. 'resources.worker.add.step1',
// 14. 'resources.worker.add.step2.tips',
// ========== End of To-Do List ==========
+6 -1
View File
@@ -124,7 +124,10 @@ export default {
'models.table.status': 'Status',
'models.form.submit.anyway': 'Submit Anyway',
'models.form.evaluating': 'Evaluating...',
'models.form.incompatible': 'Incompatible'
'models.form.incompatible': 'Incompatible',
'models.form.restart.onerror': 'Auto-Restart On Error',
'models.form.restart.onerror.tips':
'When an error occurs, it will automatically attempt to restart.'
};
// ========== To-Do: Translate Keys (Remove After Translation) ==========
@@ -132,4 +135,6 @@ export default {
// 2. 'models.form.submit.anyway',
// 3. 'models.form.evaluating',
// 4. 'models.form.incompatible',
// 5. 'models.form.restart.onerror',
// 6. 'models.form.restart.onerror.tips'
// ========== End of To-Do List ==========
+6 -3
View File
@@ -41,10 +41,11 @@ export default {
'resources.table.wokers': 'воркеры',
'resources.worker.linuxormaxos': 'Linux или macOS',
'resources.table.unified': 'Объединённая память',
'resources.worker.add.step1': 'Получить токен',
'resources.worker.add.step1':
'Get Token <span class="note-text">(Run on the server)</span>',
'resources.worker.add.step2': 'Зарегистрировать воркер',
'resources.worker.add.step2.tips':
'Примечание: <span style="color: #000;font-weight: 600">mytoken</span> — токен из первого шага.',
'Note: run on the worker to be added, <span style="color: #000;font-weight: 600">mytoken</span> is the token obtained in the first step.',
'resources.worker.add.step3':
'После успешной регистрации обновите список воркеров.',
'resources.worker.container.supported': 'Только для Linux.',
@@ -72,5 +73,7 @@ export default {
};
// ========== To-Do: Translate Keys (Remove After Translation) ==========
// 1. 'resources.modelfiles.delete.tips'
// 1. 'resources.modelfiles.delete.tips',
// 2. 'resources.worker.add.step1',
// 3. 'resources.worker.add.step2.tips
// ========== End of To-Do List ==========
+3 -1
View File
@@ -117,5 +117,7 @@ export default {
'models.table.status': '状态',
'models.form.submit.anyway': '仍然提交',
'models.form.evaluating': '评估中...',
'models.form.incompatible': '不兼容'
'models.form.incompatible': '不兼容',
'models.form.restart.onerror': '错误时重启',
'models.form.restart.onerror.tips': '当发生错误时,将自动尝试恢复'
};
+3 -2
View File
@@ -41,10 +41,11 @@ export default {
'resources.table.wokers': 'workers',
'resources.table.unified': '统一内存',
'resources.worker.linuxormaxos': 'Linux 或 MacOS',
'resources.worker.add.step1': '获取 Token',
'resources.worker.add.step1':
'获取 Token <span class="note-text">(在 server 上运行)</span>',
'resources.worker.add.step2': '注册 Worker',
'resources.worker.add.step2.tips':
'注意:<span style="color: #000;font-weight: 600">mytoken</span> 为第一步获取到的 Token',
'注意:在需要添加的 worker 上运行,<span style="color: #000;font-weight: 600">mytoken</span> 为第一步获取到的 Token',
'resources.worker.add.step3': '成功后,刷新 worker 列表即可看到新的 worker',
'resources.worker.container.supported': '不支持 MacOS 和 Windows',
'resources.worker.current.version': '当前版本为 {version}',
+15 -8
View File
@@ -5,7 +5,6 @@ import qs from 'query-string';
import {
CatalogItem,
CatalogSpec,
EvaluateResult,
EvaluateSpec,
FormData,
GPUListItem,
@@ -52,9 +51,10 @@ export async function queryModelsList(
);
}
export async function queryGPUList() {
export async function queryGPUList(params?: Global.SearchParams) {
return request<Global.PageResponse<GPUListItem>>(`/gpu-devices`, {
methos: 'GET'
methos: 'GET',
params
});
}
@@ -373,11 +373,18 @@ export async function evaluationsModelSpec(
},
options: { token: any }
) {
return request<{ results: EvaluateResult[] }>(`${MODEL_EVALUATIONS}`, {
method: 'POST',
data,
cancelToken: options?.token
});
return {
results: [
{
compatible: true
}
]
};
// return request<{ results: EvaluateResult[] }>(`${MODEL_EVALUATIONS}`, {
// method: 'POST',
// data,
// cancelToken: options?.token
// });
}
// export const evaluationsModelSpec = async (
@@ -17,6 +17,7 @@ import {
Tooltip,
Typography
} from 'antd';
import { CheckboxChangeEvent } from 'antd/es/checkbox';
import _ from 'lodash';
import React, { useCallback, useMemo } from 'react';
import {
@@ -39,6 +40,25 @@ interface AdvanceConfigProps {
source: string;
}
const CheckboxField: React.FC<{
title: string;
label: string;
checked?: boolean;
onChange?: (e: CheckboxChangeEvent) => void;
}> = ({ title, label, checked, onChange }) => {
return (
<Checkbox className="p-l-6" checked={checked} onChange={onChange}>
<Tooltip title={title}>
<span style={{ color: 'var(--ant-color-text-tertiary)' }}>{label}</span>
<QuestionCircleOutlined
className="m-l-4"
style={{ color: 'var(--ant-color-text-tertiary)' }}
/>
</Tooltip>
</Checkbox>
);
};
const AdvanceConfig: React.FC<AdvanceConfigProps> = (props) => {
const { form, isGGUF, gpuOptions, source } = props;
const { getRuleMessage } = useAppUtils();
@@ -133,21 +153,6 @@ const AdvanceConfig: React.FC<AdvanceConfigProps> = (props) => {
form.setFieldValue('backend_parameters', list);
}, []);
const handleGPUSelectorChange = (gpuIds: any[]) => {
// only handle for vllm, pick the last selected group
if (
backend === backendOptionsMap.llamaBox ||
backend === backendOptionsMap.voxBox ||
!gpuIds?.length
) {
return;
}
const lastGroupName = gpuIds[gpuIds.length - 1][0];
const lastGroupItems = gpuIds.filter((item) => item[0] === lastGroupName);
form.setFieldValue(['gpu_selector', 'gpu_ids'], lastGroupItems);
};
const collapseItems = useMemo(() => {
const children = (
<>
@@ -389,23 +394,14 @@ const AdvanceConfig: React.FC<AdvanceConfigProps> = (props) => {
style={{ padding: '0 10px', marginBottom: 0 }}
noStyle
>
<Checkbox className="p-l-6">
<Tooltip
title={intl.formatMessage({
id: 'models.form.partialoffload.tips'
})}
>
<span style={{ color: 'var(--ant-color-text-tertiary)' }}>
{intl.formatMessage({
id: 'resources.form.enablePartialOffload'
})}
</span>
<QuestionCircleOutlined
className="m-l-4"
style={{ color: 'var(--ant-color-text-tertiary)' }}
/>
</Tooltip>
</Checkbox>
<CheckboxField
title={intl.formatMessage({
id: 'models.form.partialoffload.tips'
})}
label={intl.formatMessage({
id: 'resources.form.enablePartialOffload'
})}
></CheckboxField>
</Form.Item>
</div>
)}
@@ -417,26 +413,34 @@ const AdvanceConfig: React.FC<AdvanceConfigProps> = (props) => {
style={{ padding: '0 10px', marginBottom: 0 }}
noStyle
>
<Checkbox className="p-l-6">
<Tooltip
title={intl.formatMessage({
id: 'models.form.distribution.tips'
})}
>
<span style={{ color: 'var(--ant-color-text-tertiary)' }}>
{intl.formatMessage({
id: 'resources.form.enableDistributedInferenceAcrossWorkers'
})}
</span>
<QuestionCircleOutlined
className="m-l-4"
style={{ color: 'var(--ant-color-text-tertiary)' }}
/>
</Tooltip>
</Checkbox>
<CheckboxField
title={intl.formatMessage({
id: 'models.form.distribution.tips'
})}
label={intl.formatMessage({
id: 'resources.form.enableDistributedInferenceAcrossWorkers'
})}
></CheckboxField>
</Form.Item>
</div>
)}
<div style={{ paddingBottom: 22, paddingLeft: 10 }}>
<Form.Item<FormData>
name="restart_on_error"
valuePropName="checked"
style={{ padding: '0 10px', marginBottom: 0 }}
noStyle
>
<CheckboxField
title={intl.formatMessage({
id: 'models.form.restart.onerror.tips'
})}
label={intl.formatMessage({
id: 'models.form.restart.onerror'
})}
></CheckboxField>
</Form.Item>
</div>
</>
);
return [
@@ -206,6 +206,7 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
};
const handleOnValuesChange = async (changedValues: any, allValues: any) => {
console.log('changeValues--------', changedValues);
onValuesChange?.(changedValues, allValues);
};
@@ -110,10 +110,12 @@ const AddModal: FC<AddModalProps> = (props) => {
};
const handleOnOk = async (allValues: FormData) => {
console.log('allValues---------', allValues);
if (submitAnyway.current) {
onOk(allValues);
return;
}
const result = getSourceRepoConfigValue(props.source, allValues);
const evalutionData = await handleEvaluate(result.values);
handleShowCompatibleAlert?.(evalutionData);
+2 -14
View File
@@ -447,19 +447,6 @@ export const modelLabels = [
{ label: 'Embedding', value: 'embedding_only' }
];
export const CHECK_FIELDS = new Set([
'backend',
'local_path',
'scheduleType',
'placement_strategy',
'worker_selector',
'gpu_selector',
'backend_parameters',
'backend_version',
'quantization',
'size'
]);
export const excludeFields = [
'replicas',
'categories',
@@ -468,5 +455,6 @@ export const excludeFields = [
'env',
'source',
'quantization',
'size'
'size',
'restart_on_error'
];
+1
View File
@@ -37,6 +37,7 @@ export type SourceType =
export interface FormData {
backend: string;
restart_on_error?: boolean;
env?: Record<string, any>;
size?: number;
quantization?: number;
+31 -14
View File
@@ -1,5 +1,6 @@
import { createAxiosToken } from '@/hooks/use-chunk-request';
import { queryModelFilesList } from '@/pages/resources/apis';
import { queryModelFilesList, queryWorkersList } from '@/pages/resources/apis';
import { WorkerStatusMap } from '@/pages/resources/config';
import { ListItem as WorkerListItem } from '@/pages/resources/config/types';
import { useIntl } from '@umijs/max';
import _ from 'lodash';
@@ -15,25 +16,38 @@ import { EvaluateResult, GPUListItem, ListItem } from '../config/types';
export const useGenerateFormEditInitialValues = () => {
const gpuDeviceList = useRef<any[]>([]);
const generateCascaderOptions = (list: GPUListItem[]) => {
const generateCascaderOptions = (
list: GPUListItem[],
workerList: WorkerListItem[]
) => {
const workerFields = new Set(['worker_name', 'worker_id', 'worker_ip']);
const workersMap = new Map<string, GPUListItem[]>();
for (const item of list) {
if (!workersMap.has(item.worker_name)) {
workersMap.set(item.worker_name, []);
}
workersMap.get(item.worker_name)!.push(item);
// generate a map for workerList by name to data
const workerDataMap = new Map<string, WorkerListItem>();
for (const worker of workerList) {
workerDataMap.set(worker.name, worker);
}
const workerList = Array.from(workersMap.entries()).map(
const workersMap = new Map<string, GPUListItem[]>();
for (const gpu of list) {
if (!workersMap.has(gpu.worker_name)) {
workersMap.set(gpu.worker_name, []);
}
workersMap.get(gpu.worker_name)!.push(gpu);
}
const gpuSelectorList = Array.from(workersMap.entries()).map(
([workerName, items]) => {
const firstItem = items[0];
const disDisabled =
WorkerStatusMap.ready !== workerDataMap.get(workerName)?.state;
return {
label: workerName,
label: disDisabled
? `${workerName} [${workerDataMap.get(workerName)?.state}]`
: workerName,
value: workerName,
parent: true,
disabled: disDisabled,
children: items
.map((item) => ({
label: item.name,
@@ -51,12 +65,15 @@ export const useGenerateFormEditInitialValues = () => {
}
);
return workerList;
return gpuSelectorList;
};
const getGPUList = async () => {
const data = await queryGPUList();
const gpuList = generateCascaderOptions(data.items);
const [gpuData, workerData] = await Promise.all([
queryGPUList({ page: 1, perPage: 100 }),
queryWorkersList({ page: 1, perPage: 100 })
]);
const gpuList = generateCascaderOptions(gpuData.items, workerData.items);
gpuDeviceList.current = gpuList;
return gpuList;
};
@@ -41,7 +41,7 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
width={700}
styles={{
body: {
height: 620
height: 650
}
}}
footer={null}
@@ -81,7 +81,12 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
</li>
</ul>
<h3 className="font-size-14 font-600">
1. {intl.formatMessage({ id: 'resources.worker.add.step1' })}
1.{' '}
<span
dangerouslySetInnerHTML={{
__html: intl.formatMessage({ id: 'resources.worker.add.step1' })
}}
></span>
</h3>
<HighlightCode
code={addWorkerGuide.container.getToken}
@@ -24,6 +24,7 @@ import {
modelFileActions,
onLineSourceOptions
} from '@/pages/llmodels/config/button-actions';
import { SourceType } from '@/pages/llmodels/config/types';
import DownloadModal from '@/pages/llmodels/download';
import { convertFileSize } from '@/utils';
import { useIntl, useNavigate } from '@umijs/max';
@@ -170,7 +171,7 @@ const ModelFiles = () => {
const [openDeployModal, setOpenDeployModal] = useState<{
show: boolean;
width: number | string;
source: string;
source: SourceType;
gpuOptions: any[];
modelFileOptions?: any[];
initialValues: any;
@@ -178,7 +179,7 @@ const ModelFiles = () => {
}>({
show: false,
width: 600,
source: modelSourceMap.local_path_value,
source: modelSourceMap.local_path_value as SourceType,
gpuOptions: [],
modelFileOptions: [],
initialValues: {},
@@ -23,7 +23,9 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
return (
<div className="script-install">
<h3 className="font-size-14 font-600">1. {labels.step1}</h3>
<h3 className="font-size-14 font-600">
1. <span dangerouslySetInnerHTML={{ __html: labels.step1 }}></span>
</h3>
<h4 className="font-size-13">{labels.linuxOrMac}</h4>
<HighlightCode
code={addWorkerGuide.mac.getToken}
@@ -52,7 +54,7 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
})}
theme="dark"
></HighlightCode>
<h4 className="m-t-6 font-size-13">Windows </h4>
<h4 className="m-t-6 font-size-13">Windows</h4>
<HighlightCode
theme="dark"
code={addWorkerGuide.win.registerWorker({