fix: show success message when start model falied

This commit is contained in:
jialin
2025-03-14 18:50:45 +08:00
parent 38ff16f3cd
commit db0a5875fa
12 changed files with 149 additions and 175 deletions
+5 -1
View File
@@ -2,7 +2,7 @@
padding: 6px 10px; padding: 6px 10px;
position: relative; position: relative;
padding-top: 35px; padding-top: 35px;
text-align: center; text-align: left;
border-radius: var(--border-radius-base); border-radius: var(--border-radius-base);
margin: 0; margin: 0;
border: 1px solid transparent; border: 1px solid transparent;
@@ -21,6 +21,10 @@
background-color: var(--ant-color-warning-bg); background-color: var(--ant-color-warning-bg);
} }
.content {
word-break: break-all;
}
.title { .title {
position: absolute; position: absolute;
left: 0; left: 0;
+1 -1
View File
@@ -37,7 +37,7 @@ const AlertInfo: React.FC<AlertInfoProps> = (props) => {
<WarningFilled className={classNames('info-icon', type)} /> <WarningFilled className={classNames('info-icon', type)} />
<span className="text">{title}</span> <span className="text">{title}</span>
</div> </div>
{message} <span className="content">{message}</span>
</Typography.Paragraph> </Typography.Paragraph>
</div> </div>
) : null} ) : null}
+1 -1
View File
@@ -19,7 +19,7 @@ const DeleteModal = forwardRef((props, ref) => {
title?: string; title?: string;
operation: string; operation: string;
} }
>({}); >({} as any);
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
show: ( show: (
+5 -5
View File
@@ -81,7 +81,7 @@ export default {
'models.form.filePath': 'Model Path', 'models.form.filePath': 'Model Path',
'models.form.backendVersion': 'Backend Version', 'models.form.backendVersion': 'Backend Version',
'models.form.backendVersion.tips': 'models.form.backendVersion.tips':
'Pin a specific version to keep the backend stable across GPUStack upgrades.', 'To use the desired version of vLLM/llama-box/vox-box, the system will automatically create a virtual environment in the online environment to install the corresponding version. After a GPUStack upgrade, the backend version will remain fixed. {link}',
'models.form.gpuselector': 'GPU Selector', 'models.form.gpuselector': 'GPU Selector',
'models.form.backend.llamabox': 'models.form.backend.llamabox':
'For GGUF format models, supports Linux, macOS, and Windows.', 'For GGUF format models, supports Linux, macOS, and Windows.',
@@ -98,10 +98,10 @@ export default {
'models.localpat.safe.tips.title': 'Safetensors format model', 'models.localpat.safe.tips.title': 'Safetensors format model',
'models.localpath.shared.tips.title': 'Sharded GGUF Format Model', 'models.localpath.shared.tips.title': 'Sharded GGUF Format Model',
'models.localpath.gguf.tips': 'models.localpath.gguf.tips':
' Specify the model file, e.g., /usr/local/models/model.gguf.', ' Specify the model file, e.g., /data/models/model.gguf.',
'models.localpath.safe.tips': 'models.localpath.safe.tips':
'Specify the model directory that contains .safetensors and config.json files, e.g., /usr/local/models/model/.', 'Specify the model directory that contains .safetensors and config.json files, e.g., /data/models/model/.',
'models.localpath.chunks.tips': `Specify the first shard file of the model, e.g., /usr/local/models/model-00001-of-00004.gguf.`, 'models.localpath.chunks.tips': `Specify the first shard file of the model, e.g., /data/models/model-00001-of-00004.gguf.`,
'models.form.replicas.tips': 'models.form.replicas.tips':
'Multiple replicas enable load balancing for { api } inference requests.', 'Multiple replicas enable load balancing for { api } inference requests.',
'models.table.list.empty': 'No Models yet!', 'models.table.list.empty': 'No Models yet!',
@@ -115,5 +115,5 @@ export default {
'models.form.backend.warning': 'models.form.backend.warning':
'The backend for GGUF format models uses llama-box.', 'The backend for GGUF format models uses llama-box.',
'models.form.backend.warning.llamabox': 'models.form.backend.warning.llamabox':
'Use the llama-box backend, set the absolute model path to the model file, such as <span style="font-weight: 700">/usr/local/models/model.gguf</span>. For sharded models, set the first shard file of the model, such as <span style="font-weight: 700">/usr/local/models/model-00001-of-00004.gguf</span>.' 'To use the llama-box backend, specify the full path to the model file (e.g.,<span style="font-weight: 700">/data/models/model.gguf</span>). For sharded models, provide the path to the first shard (e.g.,<span style="font-weight: 700">/data/models/model-00001-of-00004.gguf</span>).'
}; };
+3 -3
View File
@@ -81,7 +81,7 @@ export default {
'models.form.filePath': 'Путь к модели', 'models.form.filePath': 'Путь к модели',
'models.form.backendVersion': 'Версия бэкенда', 'models.form.backendVersion': 'Версия бэкенда',
'models.form.backendVersion.tips': 'models.form.backendVersion.tips':
'Фиксация версии обеспечивает стабильность бэкенда при обновлениях GPUStack.', 'TODO: Translate key "models.form.backendVersion.tips"',
'models.form.gpuselector': 'Селектор GPU', 'models.form.gpuselector': 'Селектор GPU',
'models.form.backend.llamabox': 'models.form.backend.llamabox':
'Для моделей формата GGUF. Поддержка Linux, macOS и Windows.', 'Для моделей формата GGUF. Поддержка Linux, macOS и Windows.',
@@ -97,10 +97,10 @@ export default {
'models.localpat.safe.tips.title': 'Модель формата Safetensors', 'models.localpat.safe.tips.title': 'Модель формата Safetensors',
'models.localpath.shared.tips.title': 'Шардированная GGUF-модель', 'models.localpath.shared.tips.title': 'Шардированная GGUF-модель',
'models.localpath.gguf.tips': 'models.localpath.gguf.tips':
'Укажите файл модели, например: /usr/local/models/model.gguf.', 'Укажите файл модели, например: /data/models/model.gguf.',
'models.localpath.safe.tips': 'models.localpath.safe.tips':
'Укажите директорию модели с файлами .safetensors и config.json.', 'Укажите директорию модели с файлами .safetensors и config.json.',
'models.localpath.chunks.tips': `Укажите первый шард модели, например: /usr/local/models/model-00001-of-00004.gguf.`, 'models.localpath.chunks.tips': `Укажите первый шард модели, например: /data/models/model-00001-of-00004.gguf.`,
'models.form.replicas.tips': 'models.form.replicas.tips':
'Несколько реплик обеспечивают балансировку нагрузки для { api } запросов.', 'Несколько реплик обеспечивают балансировку нагрузки для { api } запросов.',
'models.table.list.empty': 'Модели отсутствуют!', 'models.table.list.empty': 'Модели отсутствуют!',
+5 -6
View File
@@ -78,7 +78,7 @@ export default {
'models.form.filePath': '模型路径', 'models.form.filePath': '模型路径',
'models.form.backendVersion': '后端版本', 'models.form.backendVersion': '后端版本',
'models.form.backendVersion.tips': 'models.form.backendVersion.tips':
'固定指定版本以保持后端在 GPUStack 升级过程中的稳定性', '固定以使用期望的 vLLM/llama-box/vox-box 版本,在线环境会自动创建虚拟环境安装对应版本的vLLM/llama-box/vox-box。在 GPUStack 升级后也将保持固定的后端版本。{link}',
'models.form.gpuselector': 'GPU 选择器', 'models.form.gpuselector': 'GPU 选择器',
'models.form.backend.llamabox': 'models.form.backend.llamabox':
'用于 GGUF 格式模型,支持 Linux, macOS 和 Windows', '用于 GGUF 格式模型,支持 Linux, macOS 和 Windows',
@@ -93,12 +93,11 @@ export default {
'models.localpath.gguf.tips.title': 'GGUF 格式模型', 'models.localpath.gguf.tips.title': 'GGUF 格式模型',
'models.localpat.safe.tips.title': 'safetensors 格式模型', 'models.localpat.safe.tips.title': 'safetensors 格式模型',
'models.localpath.shared.tips.title': '分片的 GGUF 格式模型', 'models.localpath.shared.tips.title': '分片的 GGUF 格式模型',
'models.localpath.gguf.tips': 'models.localpath.gguf.tips': '指向模型文件,例如 /data/models/model.gguf',
'指向模型文件,例如 /usr/local/models/model.gguf',
'models.localpath.safe.tips': 'models.localpath.safe.tips':
'指向包含 .safetensors, config.json 文件的模型目录,例如 /usr/local/models/model/', '指向包含 .safetensors, config.json 文件的模型目录,例如 /data/models/model/',
'models.localpath.chunks.tips': 'models.localpath.chunks.tips':
'指向模型第一个分片文件,例如 /usr/local/models/model-00001-of-00004.gguf', '指向模型第一个分片文件,例如 /data/models/model-00001-of-00004.gguf',
'models.form.replicas.tips': '多副本数实现 { api } 接口推理请求的负载均衡', 'models.form.replicas.tips': '多副本数实现 { api } 接口推理请求的负载均衡',
'models.table.list.empty': '暂无已部署模型', 'models.table.list.empty': '暂无已部署模型',
'models.table.list.getStart': 'models.table.list.getStart':
@@ -110,5 +109,5 @@ export default {
'models.table.vram.allocated': '分配显存', 'models.table.vram.allocated': '分配显存',
'models.form.backend.warning': 'GGUF 格式模型后端用 llama-box。', 'models.form.backend.warning': 'GGUF 格式模型后端用 llama-box。',
'models.form.backend.warning.llamabox': 'models.form.backend.warning.llamabox':
'使用 llama-box 后端时,设置模型文件的绝对路径,如 <span style="font-weight: 700">/usr/local/models/model.gguf</span>。对于分片模型,设置模型的第一个分片文件,如 <span style="font-weight: 700">/usr/local/models/model-00001-of-00004.gguf</span>。' '使用 llama-box 后端,请指定模型文件的完整路径(例如:<span style="font-weight: 700">/data/models/model.gguf</span>。对于分片模型,请提供第一个分片的路径(例如:<span style="font-weight: 700">/data/models/model-00001-of-00004.gguf</span>。'
}; };
@@ -1,4 +1,5 @@
import AutoTooltip from '@/components/auto-tooltip'; import AutoTooltip from '@/components/auto-tooltip';
import IconFont from '@/components/icon-font';
import LabelSelector from '@/components/label-selector'; import LabelSelector from '@/components/label-selector';
import ListInput from '@/components/list-input'; import ListInput from '@/components/list-input';
import SealCascader from '@/components/seal-form/seal-cascader'; import SealCascader from '@/components/seal-form/seal-cascader';
@@ -326,28 +327,35 @@ const AdvanceConfig: React.FC<AdvanceConfigProps> = (props) => {
<Form.Item name="backend_version"> <Form.Item name="backend_version">
<SealInput.Input <SealInput.Input
label={intl.formatMessage({ id: 'models.form.backendVersion' })} label={intl.formatMessage({ id: 'models.form.backendVersion' })}
description={intl.formatMessage({ description={intl.formatMessage(
id: 'models.form.backendVersion.tips' {
})} id: 'models.form.backendVersion.tips'
labelExtra={ },
backendParamsTips?.releases && ( {
<span link: backendParamsTips?.releases && (
style={{ <span
marginLeft: 5 style={{
}} marginLeft: 5
> }}
(
<Typography.Link
style={{ lineHeight: 1 }}
href={backendParamsTips?.releases}
target="_blank"
> >
{intl.formatMessage({ id: 'models.form.releases' })} <Typography.Link
</Typography.Link> className="flex-center"
) style={{ lineHeight: 1 }}
</span> href={backendParamsTips?.releases}
) target="_blank"
} >
<span>
{intl.formatMessage({ id: 'models.form.releases' })}
</span>
<IconFont
type="icon-external-link"
className="font-size-14 m-l-4"
></IconFont>
</Typography.Link>
</span>
)
}
)}
></SealInput.Input> ></SealInput.Input>
</Form.Item> </Form.Item>
@@ -367,20 +375,26 @@ const AdvanceConfig: React.FC<AdvanceConfigProps> = (props) => {
dataList={form.getFieldValue('backend_parameters') || []} dataList={form.getFieldValue('backend_parameters') || []}
onChange={handleBackendParametersChange} onChange={handleBackendParametersChange}
options={paramsConfig} options={paramsConfig}
labelExtra={ description={
backendParamsTips?.link && ( backendParamsTips && (
<span style={{ marginLeft: 5 }}> <span>
( {intl.formatMessage(
{ id: 'models.form.backend_parameters.vllm.tips' },
{ backend: backendParamsTips.backend || '' }
)}{' '}
<Typography.Link <Typography.Link
style={{ lineHeight: 1 }} className="flex-center"
href={backendParamsTips?.link} href={backendParamsTips.link}
target="_blank" target="_blank"
> >
{intl.formatMessage({ <span>
id: 'models.form.moreparameters' {intl.formatMessage({ id: 'common.text.here' })}
})} </span>
<IconFont
type="icon-external-link"
className="font-size-14 m-l-4"
></IconFont>
</Typography.Link> </Typography.Link>
)
</span> </span>
) )
} }
+13 -14
View File
@@ -6,7 +6,7 @@ import TooltipList from '@/components/tooltip-list';
import { PageAction } from '@/config'; import { PageAction } from '@/config';
import { PageActionType } from '@/config/types'; import { PageActionType } from '@/config/types';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Form, Tooltip, Typography } from 'antd'; import { Form, Typography } from 'antd';
import _ from 'lodash'; import _ from 'lodash';
import React, { import React, {
forwardRef, forwardRef,
@@ -300,25 +300,24 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
defaultActiveFirstOption defaultActiveFirstOption
disabled={false} disabled={false}
options={ollamaModelOptions} options={ollamaModelOptions}
label={ description={
<> <span>
{intl.formatMessage({ id: 'model.form.ollama.model' })}{' '} <span>
{intl.formatMessage({ id: 'models.form.ollamalink' })}
</span>
<Typography.Link <Typography.Link
className="flex-center"
href="https://www.ollama.com/library" href="https://www.ollama.com/library"
target="_blank" target="_blank"
> >
<Tooltip <IconFont
title={intl.formatMessage({ id: 'models.form.ollamalink' })} type="icon-external-link"
placement="topRight" className="font-size-14"
> ></IconFont>
<IconFont
type="icon-external-link"
className="font-size-14"
></IconFont>
</Tooltip>
</Typography.Link> </Typography.Link>
</> </span>
} }
label={intl.formatMessage({ id: 'model.form.ollama.model' })}
placeholder={intl.formatMessage({ id: 'model.form.ollamaholder' })} placeholder={intl.formatMessage({ id: 'model.form.ollamaholder' })}
required required
></SealAutoComplete> ></SealAutoComplete>
@@ -248,6 +248,7 @@ const AddModal: React.FC<AddModalProps> = (props) => {
ellipsis={false} ellipsis={false}
message={ message={
<span <span
style={{}}
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: intl.formatMessage({ __html: intl.formatMessage({
id: warningStatus.message id: warningStatus.message
+56 -99
View File
@@ -403,44 +403,36 @@ const Models: React.FC<ModelsProps> = ({
}, []); }, []);
const handleStartModel = async (row: ListItem) => { const handleStartModel = async (row: ListItem) => {
try { await updateModel({
await updateModel({ id: row.id,
id: row.id, data: {
data: { ..._.omit(row, [
..._.omit(row, [ 'id',
'id', 'ready_replicas',
'ready_replicas', 'created_at',
'created_at', 'updated_at',
'updated_at', 'rowIndex'
'rowIndex' ]),
]), replicas: 1
replicas: 1 }
} });
});
} catch (error) {
// ingore
}
}; };
const handleStopModel = async (row: ListItem) => { const handleStopModel = async (row: ListItem) => {
try { await updateModel({
await updateModel({ id: row.id,
id: row.id, data: {
data: { ..._.omit(row, [
..._.omit(row, [ 'id',
'id', 'ready_replicas',
'ready_replicas', 'created_at',
'created_at', 'updated_at',
'updated_at', 'rowIndex'
'rowIndex' ]),
]), replicas: 0
replicas: 0 }
} });
}); removeExpandedRowKey([row.id]);
removeExpandedRowKey([row.id]);
} catch (error) {
// ingore
}
}; };
const handleModalOk = useCallback( const handleModalOk = useCallback(
@@ -639,32 +631,36 @@ const Models: React.FC<ModelsProps> = ({
const handleSelect = useCallback( const handleSelect = useCallback(
async (val: any, row: ListItem) => { async (val: any, row: ListItem) => {
if (val === 'edit') { try {
handleEdit(row); if (val === 'edit') {
} handleEdit(row);
if (val === 'chat') { }
handleOpenPlayGround(row); if (val === 'chat') {
} handleOpenPlayGround(row);
if (val === 'delete') { }
handleDelete(row); if (val === 'delete') {
} handleDelete(row);
if (val === 'start') { }
await handleStartModel(row); if (val === 'start') {
message.success(intl.formatMessage({ id: 'common.message.success' })); await handleStartModel(row);
updateExpandedRowKeys([row.id, ...expandedRowKeys]); message.success(intl.formatMessage({ id: 'common.message.success' }));
} updateExpandedRowKeys([row.id, ...expandedRowKeys]);
}
if (val === 'stop') { if (val === 'stop') {
modalRef.current.show({ modalRef.current.show({
content: 'models.instances', content: 'models.instances',
title: 'common.title.stop.confirm', title: 'common.title.stop.confirm',
okText: 'common.button.stop', okText: 'common.button.stop',
operation: 'common.stop.single.confirm', operation: 'common.stop.single.confirm',
name: row.name, name: row.name,
async onOk() { async onOk() {
await handleStopModel(row); await handleStopModel(row);
} }
}); });
}
} catch (error) {
// ignore
} }
}, },
[handleEdit, handleOpenPlayGround, handleDelete, expandedRowKeys] [handleEdit, handleOpenPlayGround, handleDelete, expandedRowKeys]
@@ -998,45 +994,6 @@ const Models: React.FC<ModelsProps> = ({
disabled={!rowSelection.selectedRowKeys.length} disabled={!rowSelection.selectedRowKeys.length}
onSelect={handleActionSelect} onSelect={handleActionSelect}
/> />
{/* <Button
icon={<IconFont type="icon-outline-play"></IconFont>}
onClick={handleStartBatch}
disabled={!rowSelection.selectedRows.length}
>
<span>
{intl?.formatMessage?.({ id: 'common.button.start' })}
{rowSelection.selectedRows.length > 0 && (
<span>({rowSelection.selectedRows?.length})</span>
)}
</span>
</Button>
<Button
icon={<IconFont type="icon-stop1"></IconFont>}
onClick={handleStopBatch}
disabled={!rowSelection.selectedRows.length}
>
<span>
{intl?.formatMessage?.({ id: 'common.button.stop' })}
{rowSelection.selectedRows.length > 0 && (
<span>({rowSelection.selectedRows?.length})</span>
)}
</span>
</Button>
<Access accessible={access.canDelete}>
<Button
icon={<DeleteOutlined />}
danger
onClick={handleDeleteBatch}
disabled={!rowSelection.selectedRowKeys.length}
>
<span>
{intl?.formatMessage?.({ id: 'common.button.delete' })}
{rowSelection.selectedRowKeys.length > 0 && (
<span>({rowSelection.selectedRowKeys?.length})</span>
)}
</span>
</Button>
</Access> */}
</Space> </Space>
} }
></PageTools> ></PageTools>
+13 -14
View File
@@ -7,7 +7,7 @@ import SealSelect from '@/components/seal-form/seal-select';
import { PageAction } from '@/config'; import { PageAction } from '@/config';
import { PageActionType } from '@/config/types'; import { PageActionType } from '@/config/types';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Form, Modal, Tooltip, Typography } from 'antd'; import { Form, Modal, Typography } from 'antd';
import _ from 'lodash'; import _ from 'lodash';
import React, { useEffect, useMemo, useRef, useState } from 'react'; import React, { useEffect, useMemo, useRef, useState } from 'react';
import { import {
@@ -236,25 +236,24 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
disabled={false} disabled={false}
options={ollamaModelOptions} options={ollamaModelOptions}
placeholder={intl.formatMessage({ id: 'model.form.ollamaholder' })} placeholder={intl.formatMessage({ id: 'model.form.ollamaholder' })}
label={ description={
<> <span>
{intl.formatMessage({ id: 'model.form.ollama.model' })}{' '} <span>
{intl.formatMessage({ id: 'models.form.ollamalink' })}
</span>
<Typography.Link <Typography.Link
className="flex-center"
href="https://www.ollama.com/library" href="https://www.ollama.com/library"
target="_blank" target="_blank"
> >
<Tooltip <IconFont
title={intl.formatMessage({ id: 'models.form.ollamalink' })} type="icon-external-link"
placement="topRight" className="font-size-14"
> ></IconFont>
<IconFont
type="icon-external-link"
className="font-size-14"
></IconFont>
</Tooltip>
</Typography.Link> </Typography.Link>
</> </span>
} }
label={intl.formatMessage({ id: 'model.form.ollama.model' })}
required required
></SealAutoComplete> ></SealAutoComplete>
</Form.Item> </Form.Item>
+1
View File
@@ -22,6 +22,7 @@ export const requestConfig: RequestConfig = {
history.push('/login', { replace: true }); history.push('/login', { replace: true });
} }
console.log('error==============', error);
} }
}, },
requestInterceptors: [ requestInterceptors: [