chore: the backend matches the model file

This commit is contained in:
jialin
2025-03-12 10:43:59 +08:00
parent 5edbc1a7b5
commit 0fc570b138
16 changed files with 285 additions and 132 deletions
@@ -35,13 +35,13 @@ const NoteInfo: React.FC<NoteInfoProps> = (props) => {
) : (
<>
<span>{label}</span>
<span className="note-info">
{required && (
{required && (
<span className="note-info">
<span style={{ color: 'red' }} className="star">
*
</span>
)}
</span>
</span>
)}
</>
)}
{labelExtra}
+3 -1
View File
@@ -230,5 +230,7 @@ export default {
'common.search.empty': 'No matching results found.',
'common.button.downloadLog': 'Download Log',
'common.button.faq': 'FAQ',
'common.button.moreInfo': 'More Info'
'common.button.moreInfo': 'More Info',
'common.text.warning': 'Warning',
'common.text.error': 'Error'
};
+6 -1
View File
@@ -110,5 +110,10 @@ export default {
'models.table.llamaAcrossworker': 'Llama-box Across Workers',
'models.table.vllmAcrossworker': 'vLLM Across Workers',
'models.form.releases': 'Releases',
'models.form.moreparameters': 'More Parameters'
'models.form.moreparameters': 'Parameter Description',
'models.table.vram.allocated': 'Allocated VRAM',
'models.form.backend.warning':
'The backend for GGUF format models uses llama-box.',
'models.form.backend.warning.llamabox':
'Llama-box only supports GGUF format models. If your model files were downloaded from Ollama, you can ignore this warning.'
};
+3 -1
View File
@@ -228,5 +228,7 @@ export default {
'common.search.empty': 'Ничего не найдено',
'common.button.downloadLog': 'Скачать логи',
'common.button.faq': 'TODO: Translate key "common.button.faq"',
'common.button.moreInfo': 'TODO: Translate key "common.button.moreInfo"'
'common.button.moreInfo': 'TODO: Translate key "common.button.moreInfo"',
'common.text.warning': 'TODO: Translate key "common.text.warning"',
'common.text.error': 'TODO: Translate key "common.text.error"'
};
+3 -1
View File
@@ -223,5 +223,7 @@ export default {
'common.search.empty': '未找到匹配结果',
'common.button.downloadLog': '下载日志',
'common.button.faq': '常见问题',
'common.button.moreInfo': '更多信息'
'common.button.moreInfo': '更多信息',
'common.text.warning': '警告',
'common.text.error': '错误'
};
+5 -1
View File
@@ -106,5 +106,9 @@ export default {
'models.table.llamaAcrossworker': 'Llama-box 跨节点',
'models.table.vllmAcrossworker': 'vLLM 跨节点',
'models.form.releases': '版本',
'models.form.moreparameters': '更多参数'
'models.form.moreparameters': '参数说明',
'models.table.vram.allocated': '分配显存',
'models.form.backend.warning': 'GGUF 格式模型后端用 llama-box。',
'models.form.backend.warning.llamabox':
'llama-box 仅支持 GGUF 格式模型,如果您的模型是从 Ollama 下载的模型文件,忽略此警告。'
};
@@ -109,7 +109,7 @@ const AdvanceConfig: React.FC<AdvanceConfigProps> = (props) => {
return {
backend: 'vLLM',
releases: 'https://github.com/vllm-project/vllm/releases',
link: 'https://docs.vllm.ai/en/stable/serving/openai_compatible_server.html#command-line-arguments-for-the-server'
link: 'https://docs.vllm.ai/en/stable/serving/openai_compatible_server.html#cli-reference'
};
}
return null;
@@ -369,7 +369,7 @@ const AdvanceConfig: React.FC<AdvanceConfigProps> = (props) => {
options={paramsConfig}
labelExtra={
backendParamsTips?.link && (
<span style={{ marginLeft: 2 }}>
<span style={{ marginLeft: 5 }}>
(
<Typography.Link
style={{ lineHeight: 1 }}
@@ -1,36 +1,41 @@
import useOverlayScroller from '@/hooks/use-overlay-scroller';
import React from 'react';
import SimpleBar from 'simplebar-react';
import 'simplebar-react/dist/simplebar.min.css';
import '../style/column-wrapper.less';
const ColumnWrapper: React.FC<any> = ({ children, footer, height }) => {
const handleOnWheel = (e: React.WheelEvent) => {
console.log('handleOnWheel', e);
e.preventDefault();
e.stopPropagation();
};
const ColumnWrapper: React.FC<any> = ({
children,
footer,
maxHeight,
paddingBottom = 50
}) => {
const scroller = React.useRef<any>(null);
const { initialize } = useOverlayScroller();
React.useEffect(() => {
if (scroller.current) {
initialize(scroller.current);
}
}, []);
return (
<>
{footer ? (
<div className="column-wrapper-footer">
<div className="column-wrapper">
<SimpleBar
<div className="column-wrapper" ref={scroller} style={{ maxHeight }}>
<div
style={{
maxHeight: height || 'calc(100vh - 89px)',
paddingBottom: '50px'
paddingBottom: paddingBottom
}}
>
{children}
</SimpleBar>
</div>
</div>
{<div className="footer">{footer}</div>}
</div>
) : (
<div className="column-wrapper">
<SimpleBar style={{ maxHeight: height || 'calc(100vh - 89px)' }}>
{children}
</SimpleBar>
<div className="column-wrapper" ref={scroller} style={{ maxHeight }}>
<div>{children}</div>
</div>
)}
</>
+4 -1
View File
@@ -635,7 +635,10 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
label: 'vox-box',
value: backendOptionsMap.voxBox,
disabled:
props.source === modelSourceMap.ollama_library_value || isGGUF
props.source === modelSourceMap.local_path_value
? false
: props.source === modelSourceMap.ollama_library_value ||
isGGUF
}
]
}
+103 -34
View File
@@ -1,3 +1,4 @@
import AlertBlockInfo from '@/components/alert-info/block';
import ModalFooter from '@/components/modal-footer';
import { PageActionType } from '@/config/types';
import { CloseOutlined } from '@ant-design/icons';
@@ -47,6 +48,13 @@ const AddModal: React.FC<AddModalProps> = (props) => {
const [collapsed, setCollapsed] = useState<boolean>(false);
const [isGGUF, setIsGGUF] = useState<boolean>(false);
const modelFileRef = useRef<any>(null);
const [warningStatus, setWarningStatus] = useState<{
show: boolean;
message: string;
}>({
show: false,
message: ''
});
const handleSelectModelFile = useCallback((item: any) => {
form.current?.setFieldValue?.('file_name', item.fakeName);
@@ -71,6 +79,35 @@ const AddModal: React.FC<AddModalProps> = (props) => {
}
};
const updateShowWarning = () => {
const backend = form.current?.getFieldValue?.('backend');
const localPath = form.current?.getFieldValue?.('local_path');
if (source !== modelSourceMap.local_path_value || !localPath) {
return;
}
if (localPath.endsWith('.gguf') && backend !== backendOptionsMap.llamaBox) {
setWarningStatus({
show: true,
message: 'models.form.backend.warning'
});
} else if (
!localPath.endsWith('.gguf') &&
backend === backendOptionsMap.llamaBox
) {
setWarningStatus({
show: true,
message: 'models.form.backend.warning.llamabox'
});
} else {
setWarningStatus({
show: false,
message: ''
});
}
};
const handleBackendChange = (backend: string) => {
if (backend === backendOptionsMap.vllm) {
setIsGGUF(false);
@@ -79,6 +116,7 @@ const AddModal: React.FC<AddModalProps> = (props) => {
if (backend === backendOptionsMap.llamaBox) {
setIsGGUF(true);
}
updateShowWarning();
};
const handleCancel = useCallback(() => {
@@ -92,6 +130,10 @@ const AddModal: React.FC<AddModalProps> = (props) => {
useEffect(() => {
if (!open) {
setIsGGUF(false);
setWarningStatus({
show: false,
message: ''
});
form.current?.setFieldValue?.('backend', backendOptionsMap.vllm);
} else if (source === modelSourceMap.ollama_library_value) {
form.current?.setFieldValue?.('backend', backendOptionsMap.llamaBox);
@@ -144,7 +186,13 @@ const AddModal: React.FC<AddModalProps> = (props) => {
<div style={{ display: 'flex', height: '100%' }}>
{SEARCH_SOURCE.includes(props.source) && (
<>
<div style={{ display: 'flex', flex: 1 }}>
<div
style={{
display: 'flex',
flex: 1,
maxWidth: '33.33%'
}}
>
<ColumnWrapper>
<SearchModel
modelSource={props.source}
@@ -153,7 +201,13 @@ const AddModal: React.FC<AddModalProps> = (props) => {
</ColumnWrapper>
<Separator></Separator>
</div>
<div style={{ display: 'flex', flex: 1 }}>
<div
style={{
display: 'flex',
flex: 1,
maxWidth: '33.33%'
}}
>
<ColumnWrapper>
<ModelCard
selectedModel={selectedModel}
@@ -176,38 +230,53 @@ const AddModal: React.FC<AddModalProps> = (props) => {
</div>
</>
)}
<ColumnWrapper
footer={
<ModalFooter
onCancel={handleCancel}
onOk={handleSumit}
style={{
padding: '16px 24px',
display: 'flex',
justifyContent: 'flex-end'
}}
></ModalFooter>
}
>
<>
{SEARCH_SOURCE.includes(source) && (
<TitleWrapper>
{intl.formatMessage({ id: 'models.form.configurations' })}
<span style={{ display: 'flex', height: 24 }}></span>
</TitleWrapper>
)}
<DataForm
source={source}
action={action}
selectedModel={selectedModel}
onOk={onOk}
ref={form}
isGGUF={isGGUF}
gpuOptions={props.gpuOptions}
onBackendChange={handleBackendChange}
></DataForm>
</>
</ColumnWrapper>
<div style={{ display: 'flex', flex: 1, maxWidth: '100%' }}>
<ColumnWrapper
paddingBottom={warningStatus.show ? 125 : 50}
footer={
<>
{warningStatus.show && (
<AlertBlockInfo
ellipsis={false}
message={intl.formatMessage({ id: warningStatus.message })}
title={intl.formatMessage({
id: 'common.text.warning'
})}
type="warning"
></AlertBlockInfo>
)}
<ModalFooter
onCancel={handleCancel}
onOk={handleSumit}
style={{
padding: '16px 24px',
display: 'flex',
justifyContent: 'flex-end'
}}
></ModalFooter>
</>
}
>
<>
{SEARCH_SOURCE.includes(source) && (
<TitleWrapper>
{intl.formatMessage({ id: 'models.form.configurations' })}
<span style={{ display: 'flex', height: 24 }}></span>
</TitleWrapper>
)}
<DataForm
source={source}
action={action}
selectedModel={selectedModel}
onOk={onOk}
ref={form}
isGGUF={isGGUF}
gpuOptions={props.gpuOptions}
onBackendChange={handleBackendChange}
></DataForm>
</>
</ColumnWrapper>
</div>
</div>
</Drawer>
);
+32 -38
View File
@@ -126,20 +126,14 @@ const distributeCols: ColumnProps[] = [
key: 'gpu_index'
},
{
title: 'resources.table.vram',
title: 'models.table.vram.allocated',
locale: true,
key: 'vram',
rowSpan: ({ row, rowIndex, colIndex, dataIndex, dataList }) => {
return rowIndex === 0 ? dataList.length : 0;
},
render: ({ rowIndex, dataList }) => {
if (rowIndex === 0) {
return convertFileSize(
_.sumBy(dataList, (item: any) => item.vram),
2
);
}
return null;
// rowSpan: ({ row, rowIndex, colIndex, dataIndex, dataList }) => {
// return rowIndex === 0 ? dataList.length : 0;
// },
render: ({ rowIndex, row, dataList }) => {
return convertFileSize(row.vram, 1);
}
}
];
@@ -297,33 +291,33 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
const renderDistributedServer = useCallback(
(severList: any[]) => {
const list = _.map(severList, (item: any) => {
const data = _.find(workerList, { id: item.worker_id });
return {
worker_name: data?.name,
worker_ip: data?.ip,
port: '',
vram: calcTotalVram(item.computed_resource_claim?.vram || {}),
gpu_index: _.keys(item.computed_resource_claim?.vram).join(',')
};
});
// const list = _.map(severList, (item: any) => {
// const data = _.find(workerList, { id: item.worker_id });
// return {
// worker_name: data?.name,
// worker_ip: data?.ip,
// port: '',
// vram: calcTotalVram(item.computed_resource_claim?.vram || {}),
// gpu_index: _.keys(item.computed_resource_claim?.vram).join(',')
// };
// });
// const list = [
// {
// worker_name: 'worker1',
// worker_ip: '192.168.50.23',
// port: '',
// vram: 21555525632,
// gpu_index: '0,1'
// },
// {
// worker_name: 'worker2',
// worker_ip: '192.168.50.25',
// port: '',
// vram: 21555525632,
// gpu_index: '2,3'
// }
// ];
const list = [
{
worker_name: 'worker1',
worker_ip: '192.168.50.23',
port: '',
vram: 21555525632,
gpu_index: '0,1'
},
{
worker_name: 'worker2',
worker_ip: '192.168.50.25',
port: '',
vram: 21555525632,
gpu_index: '2,3'
}
];
const mainWorker = [
{
+6 -2
View File
@@ -459,7 +459,9 @@ const Models: React.FC<ModelsProps> = ({
});
setOpenAddModal(false);
message.success(intl.formatMessage({ id: 'common.message.success' }));
handleSearch();
setTimeout(() => {
handleSearch();
}, 150);
restoreScrollHeight();
} catch (error) {}
},
@@ -499,7 +501,9 @@ const Models: React.FC<ModelsProps> = ({
updateExpandedRowKeys([modelData.id, ...expandedRowKeys]);
}, 300);
message.success(intl.formatMessage({ id: 'common.message.success' }));
handleSearch?.();
setTimeout(() => {
handleSearch?.();
}, 150);
} catch (error) {}
},
[openDeployModal]
+89 -28
View File
@@ -1,3 +1,4 @@
import AlertBlockInfo from '@/components/alert-info/block';
import IconFont from '@/components/icon-font';
import ModalFooter from '@/components/modal-footer';
import SealAutoComplete from '@/components/seal-form/auto-complete';
@@ -8,9 +9,7 @@ import { PageActionType } from '@/config/types';
import { useIntl } from '@umijs/max';
import { Form, Modal, Tooltip, Typography } from 'antd';
import _ from 'lodash';
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
import SimpleBar from 'simplebar-react';
import 'simplebar-react/dist/simplebar.min.css';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import {
backendOptionsMap,
modelSourceMap,
@@ -18,6 +17,7 @@ import {
} from '../config';
import { FormData, ListItem } from '../config/types';
import AdvanceConfig from './advance-config';
import ColumnWrapper from './column-wrapper';
type AddModalProps = {
title: string;
@@ -73,6 +73,13 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
const [form] = Form.useForm();
const intl = useIntl();
const localPathCache = useRef<string>('');
const [warningStatus, setWarningStatus] = useState<{
show: boolean;
message: string;
}>({
show: false,
message: ''
});
const handleSetGPUIds = (backend: string) => {
if (backend === backendOptionsMap.llamaBox) {
@@ -88,7 +95,36 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
}
};
const handleBackendChange = useCallback((val: string) => {
const updateShowWarning = () => {
const backend = form.getFieldValue?.('backend');
const localPath = form.getFieldValue?.('local_path');
if (formData?.source !== modelSourceMap.local_path_value || !localPath) {
return;
}
if (localPath.endsWith('.gguf') && backend !== backendOptionsMap.llamaBox) {
setWarningStatus({
show: true,
message: 'models.form.backend.warning'
});
} else if (
!localPath.endsWith('.gguf') &&
backend === backendOptionsMap.llamaBox
) {
setWarningStatus({
show: true,
message: 'models.form.backend.warning.llamabox'
});
} else {
setWarningStatus({
show: false,
message: ''
});
}
};
const handleBackendChange = (val: string) => {
if (val === backendOptionsMap.llamaBox) {
form.setFieldsValue({
distributed_inference_across_workers: true,
@@ -97,7 +133,8 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
}
form.setFieldValue('backend_version', '');
handleSetGPUIds(val);
}, []);
updateShowWarning();
};
const handleOnFocus = () => {
localPathCache.current = form.getFieldValue('local_path');
@@ -188,23 +225,25 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
defaultActiveFirstOption
disabled={false}
options={ollamaModelOptions}
label={intl.formatMessage({ id: 'model.form.ollama.model' })}
placeholder={intl.formatMessage({ id: 'model.form.ollamaholder' })}
addAfter={
<Typography.Link
href="https://www.ollama.com/library"
target="_blank"
>
<Tooltip
title={intl.formatMessage({ id: 'models.form.ollamalink' })}
placement="topRight"
label={
<>
{intl.formatMessage({ id: 'model.form.ollama.model' })}{' '}
<Typography.Link
href="https://www.ollama.com/library"
target="_blank"
>
<IconFont
type="icon-external-link"
className="font-size-14"
></IconFont>
</Tooltip>
</Typography.Link>
<Tooltip
title={intl.formatMessage({ id: 'models.form.ollamalink' })}
placement="topRight"
>
<IconFont
type="icon-external-link"
className="font-size-14"
></IconFont>
</Tooltip>
</Typography.Link>
</>
}
required
></SealAutoComplete>
@@ -336,6 +375,12 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
if (open && formData) {
form.setFieldsValue(formData);
}
if (!open) {
setWarningStatus({
show: false,
message: ''
});
}
}, [open, formData]);
return (
@@ -353,7 +398,7 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
width={600}
styles={{
content: {
padding: '0px'
padding: '0 0 16px 0'
},
header: {
padding: 'var(--ant-modal-content-padding)',
@@ -363,17 +408,33 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
padding: '0'
},
footer: {
padding: '0 var(--ant-modal-content-padding)'
padding: '16px 24px',
margin: '0'
}
}}
footer={
<ModalFooter onCancel={onCancel} onOk={handleSumit}></ModalFooter>
<>
<ModalFooter onCancel={onCancel} onOk={handleSumit}></ModalFooter>
</>
}
>
<SimpleBar
style={{
maxHeight: '550px'
}}
<ColumnWrapper
maxHeight={550}
paddingBottom={warningStatus.show ? 85 : 0}
footer={
<>
{warningStatus.show && (
<AlertBlockInfo
ellipsis={false}
message={intl.formatMessage({ id: warningStatus.message })}
title={intl.formatMessage({
id: 'common.text.warning'
})}
type="warning"
></AlertBlockInfo>
)}
</>
}
>
<Form
name="addModalForm"
@@ -519,7 +580,7 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
isGGUF={formData?.backend === backendOptionsMap.llamaBox}
></AdvanceConfig>
</Form>
</SimpleBar>
</ColumnWrapper>
</Modal>
);
};
@@ -1,6 +1,8 @@
.column-wrapper {
flex: 1;
position: relative;
height: 100%;
overflow-y: auto;
}
.simplebar-scrollbar::before {
@@ -1,6 +1,6 @@
import IconFont from '@/components/icon-font';
import HotKeys, { KeyMap } from '@/config/hotkeys';
import { ClearOutlined, EnterOutlined, SwapOutlined } from '@ant-design/icons';
import { ClearOutlined, SendOutlined, SwapOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Button, Checkbox, Divider, Input, Tooltip } from 'antd';
import _ from 'lodash';
@@ -425,7 +425,7 @@ const MessageInput: React.FC<MessageInputProps> = forwardRef(
disabled={isDisabled}
>
{submitIcon ?? (
<EnterOutlined rotate={0} className="font-size-14" />
<SendOutlined rotate={0} className="font-size-14" />
)}
</Button>
</Tooltip>