chore: extract a model file item

main
jialin 1 year ago
parent 5766a50bc2
commit d62e43790c

@ -30,7 +30,7 @@
&.success {
border: 1px solid var(--color-progress-green);
color: var(--color-progress-green);
background: var(--ant-green-1);
background: var(--color-green-fill-light);
.content.success {
font-weight: var(--font-weight-normal);

@ -146,7 +146,7 @@ export const FilterBar: React.FC<FilterBarProps> = (props) => {
</Button>
</Space>
);
}, [actionItems, actionType]);
}, [actionItems, actionType, rowSelection?.selectedRowKeys]);
return (
<PageTools

@ -85,7 +85,7 @@ const InputWrapper = styled.div`
input.ant-input-number-input {
flex: 1;
height: ${INPUTHEIGHT}px !important;
padding-block: 5px 4px;
padding-block: 6px 4px;
padding-inline: ${INPUT_INNER_PADDING}px;
}
.ant-input-group {

@ -34,7 +34,6 @@
top: 0;
bottom: 0;
height: 100%;
// background-color: rgba(0, 0, 0, 15%);
background-color: #e0f5ec;
}

@ -24,7 +24,7 @@ export const StatusColorMap: Record<
},
success: {
text: `var(--color-progress-green)`,
bg: `var(--ant-green-1)`
bg: `var(--color-green-fill-light)`
},
inactive: {
text: `var(--ant-color-text-tertiary)`,

@ -85,6 +85,7 @@ html {
--color-chart-glod: rgba(250, 173, 20, 80%);
--seal-transition-func: cubic-bezier(0, 0, 1, 1);
--color-progress-green: rgba(84, 204, 152, 100%);
--color-green-fill-light: rgb(243 251 248);
--color-border-1: rgba(217, 217, 217, 100%);
--ant-rate-star-color: #fadb14;
--color-fill-mask: rgba(255, 255, 255, 60%);

@ -64,7 +64,7 @@ export default {
'models.search.vllm.tips':
'Non-GGUF models use vox-box for audio and vLLM(x86 Linux only) for others.',
'models.search.voxbox.tips':
'To deploy an audio model, uncheck the GGUF checkbox.',
'To deploy an audio model, uncheck the checkbox.',
'models.form.ollamalink': 'Find More in Ollama Library',
'models.form.backend_parameters.llamabox.placeholder':
'e.g., --ctx-size=8192',
@ -131,6 +131,7 @@ export default {
'models.form.check.passed': 'Compatibility Check Passed',
'models.form.check.claims':
'The model requires approximately {vram} VRAM and {ram} RAM.',
'models.form.check.claims2': 'The model requires approximately {vram} VRAM.',
'models.form.update.tips':
'Changes will only apply after you delete and recreate the instance.'
};

@ -20,7 +20,7 @@ export default {
'resources.form.binpack.tips':
'Prioritize the overall utilization of cluster resources, reducing resource fragmentation on GPUs/Workers.',
'resources.form.workerSelector.description':
'The system selects the most suitable GPU or Worker for deploying model instances based on predefined labels.',
'The system selects the most suitable Worker for deploying model instances based on predefined labels.',
'resources.table.ip': 'IP',
'resources.table.cpu': 'CPU',
'resources.table.memory': 'RAM',

@ -128,6 +128,7 @@ export default {
'models.form.check.passed': 'Compatibility Check Passed',
'models.form.check.claims':
'The model requires approximately {vram} VRAM and {ram} RAM.',
'models.form.check.claims2': 'The model requires approximately {vram} VRAM.',
'models.form.update.tips':
'Changes will only apply after you delete and recreate the instance.'
};
@ -146,5 +147,6 @@ export default {
// 11. 'models.form.check.passed',
// 12. 'models.form.check.claims',
// 13. 'model.deploy.search.placeholder',
// 14. 'models.form.update.tips
// 14. 'models.form.update.tips,
// 15. models.form.check.claims2
// ========== End of To-Do List ==========

@ -20,7 +20,7 @@ export default {
'resources.form.binpack.tips':
'クラスターリソースの全体的な利用率を優先し、ワーカー/GPU上のリソース断片化を減らします。',
'resources.form.workerSelector.description':
'システムは、事前定義されたラベルに基づいて、モデルインスタンスをデプロイするために最適なGPUまたはワーカーを選択します。',
'The system selects the most suitable Worker for deploying model instances based on predefined labels.',
'resources.table.ip': 'IP',
'resources.table.cpu': 'CPU',
'resources.table.memory': 'メモリ',
@ -93,4 +93,5 @@ export default {
// 14. 'resources.worker.add.step2.tips',
// 15. 'resources.modelfiles.copy.tips',
// 16. 'resources.filter.path',
// 17. 'resources.form.workerSelector.description
// ========== End of To-Do List ==========

@ -131,6 +131,7 @@ export default {
'models.form.check.passed': 'Compatibility Check Passed',
'models.form.check.claims':
'Модель требует примерно {vram} VRAM и {ram} RAM.',
'models.form.check.claims2': 'Модель требует примерно {vram} VRAM.',
'models.form.update.tips':
'Changes will only apply after you delete and recreate the instance.'
};

@ -20,7 +20,7 @@ export default {
'resources.form.binpack.tips':
'Максимизирует утилизацию ресурсов, уменьшая фрагментацию на воркерах/GPU.',
'resources.form.workerSelector.description':
'Система выбирает подходящие GPU/воркеры для развертывания моделей на основе меток.',
'Система выбирает подходящие воркеры для развертывания моделей на основе меток.',
'resources.table.ip': 'IP-адрес',
'resources.table.cpu': 'CPU',
'resources.table.memory': 'ОЗУ',

@ -60,10 +60,10 @@ export default {
'models.form.backend': '后端',
'models.form.backend_parameters': '后端参数',
'models.search.gguf.tips':
'GGUF 模型用 llama-box(支持 Linux, macOS 和 Windows)。',
'GGUF 模型用 llama-box(支持 Linux, macOS 和 Windows。',
'models.search.vllm.tips':
' 非 GGUF 的语音模型用 vox-box其它非 GGUF 的模型用 vLLM(仅支持 x86 Linux)。',
'models.search.voxbox.tips': '若需部语音模型取消勾选 GGUF 复选框。',
' 非 GGUF 的语音模型用 vox-box其它非 GGUF 的模型用 vLLM(仅支持 x86 Linux。',
'models.search.voxbox.tips': '若需部语音模型取消勾选。',
'models.form.ollamalink': '在 Ollama Library 中查找',
'models.form.backend_parameters.llamabox.placeholder':
'例如,--ctx-size=8192',
@ -123,5 +123,6 @@ export default {
'models.form.check.params': '正在校验配置...',
'models.form.check.passed': '兼容性检查通过',
'models.form.check.claims': '该模型大约需要 {vram} 显存和 {ram} 内存。',
'models.form.check.claims2': '该模型大约需要 {vram} 显存。',
'models.form.update.tips': '更改仅在删除并重新创建实例后生效。'
};

@ -20,7 +20,7 @@ export default {
'resources.form.binpack.tips':
'优先考虑整体集群的资源最大化利用,减少 GPU/Worker 上的资源碎片。',
'resources.form.workerSelector.description':
'系统在部署模型实例时,会根据预定义的标签来选择最符合要求的 GPU 或 Worker。',
'系统在部署模型实例时,会根据预定义的标签来选择最符合要求的 Worker。',
'resources.table.ip': 'IP',
'resources.table.cpu': 'CPU',
'resources.table.memory': '内存',

@ -1,9 +1,6 @@
import { createAxiosToken } from '@/hooks/use-chunk-request';
import { convertFileSize } from '@/utils';
import { InfoCircleOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Col, Empty, Row, Select, Spin, Tag, Tooltip } from 'antd';
import classNames from 'classnames';
import { Empty, Select, Spin } from 'antd';
import _ from 'lodash';
import React, {
forwardRef,
@ -16,18 +13,25 @@ import React, {
} from 'react';
import SimpleBar from 'simplebar-react';
import 'simplebar-react/dist/simplebar.min.css';
import styled from 'styled-components';
import {
evaluationsModelSpec,
queryHuggingfaceModelFiles,
queryModelScopeModelFiles
} from '../apis';
import { modelSourceMap } from '../config';
import { getFileType } from '../config/file-type';
import '../style/hf-model-file.less';
import FileParts from './file-parts';
import IncompatiableInfo from './incompatiable-info';
import ModelFileItem from './model-file-item';
import TitleWrapper from './title-wrapper';
const ItemFileWrapper = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
gap: 24px;
`;
interface HFModelFileProps {
isDownload?: boolean;
selectedModel: any;
@ -44,35 +48,6 @@ const filterReg = /\.(safetensors|gguf)$/i;
const includeReg = /\.(safetensors|gguf)$/i;
const filterRegGGUF = /\.(gguf)$/i;
const FilePartsTag = (props: { parts: any[] }) => {
if (!props.parts || !props.parts.length) {
return null;
}
const { parts } = props;
return (
<Tooltip
overlayInnerStyle={{
width: 180,
padding: 0
}}
title={<FileParts fileList={parts}></FileParts>}
>
<Tag
className="tag-item"
color="purple"
style={{
marginRight: 0
}}
>
<span style={{ opacity: 1 }}>
<InfoCircleOutlined className="m-r-5" />
{parts.length} parts
</span>
</Tag>
</Tooltip>
);
};
const HFModelFile: React.FC<HFModelFileProps> = forwardRef((props, ref) => {
const { collapsed, modelSource, isDownload } = props;
const intl = useIntl();
@ -332,28 +307,6 @@ const HFModelFile: React.FC<HFModelFileProps> = forwardRef((props, ref) => {
setDataSource({ ...dataSource, fileList: list });
};
const getModelQuantizationType = useCallback((item: any) => {
let path = item.path;
if (item?.parts?.length) {
path = `${item.path}.gguf`;
}
const quanType = getFileType(path);
if (quanType) {
return (
<Tag
className="tag-item"
color="cyan"
style={{
marginRight: 0
}}
>
{_.toUpper(quanType)}
</Tag>
);
}
return null;
}, []);
const handleOnEnter = (e: any, item: any) => {
e.stopPropagation();
if (e.key === 'Enter') {
@ -418,43 +371,20 @@ const HFModelFile: React.FC<HFModelFileProps> = forwardRef((props, ref) => {
>
<div style={{ padding: '16px 24px' }}>
{dataSource.fileList.length ? (
<Row gutter={[16, 24]}>
<ItemFileWrapper>
{_.map(dataSource.fileList, (item: any) => {
return (
<Col span={24} key={item.path}>
<div
className={classNames('hf-model-file', {
active: item.path === current
})}
tabIndex={0}
onClick={() => handleSelectModelFile(item)}
onKeyDown={(e) => handleOnEnter(e, item)}
>
<div className="title">{item.path}</div>
<div className="tags flex-between">
<span className="flex-center gap-8">
<Tag
className="tag-item"
color="green"
style={{
marginRight: 0
}}
>
{convertFileSize(item.size)}
</Tag>
{getModelQuantizationType(item)}
<FilePartsTag parts={item.parts}></FilePartsTag>
</span>
<IncompatiableInfo
isEvaluating={isEvaluating}
data={item.evaluateResult}
></IncompatiableInfo>
</div>
</div>
</Col>
<ModelFileItem
key={item.path}
data={item}
isEvaluating={isEvaluating}
active={item.path === current}
handleSelectModelFile={handleSelectModelFile}
handleOnEnter={handleOnEnter}
></ModelFileItem>
);
})}
</Row>
</ItemFileWrapper>
) : (
!dataSource.loading &&
!dataSource.fileList.length && (

@ -0,0 +1,114 @@
import { convertFileSize } from '@/utils';
import { InfoCircleOutlined } from '@ant-design/icons';
import { Tag, Tooltip } from 'antd';
import classNames from 'classnames';
import _ from 'lodash';
import React from 'react';
import 'simplebar-react/dist/simplebar.min.css';
import { getFileType } from '../config/file-type';
import '../style/hf-model-file.less';
import FileParts from './file-parts';
import IncompatiableInfo from './incompatiable-info';
interface ModelFileItemProps {
data: Record<string, any>;
isEvaluating: boolean;
active: boolean;
handleSelectModelFile: (item: any) => void;
handleOnEnter: (e: any, item: any) => void;
}
const FilePartsTag = (props: { parts: any[] }) => {
if (!props.parts || !props.parts.length) {
return null;
}
const { parts } = props;
return (
<Tooltip
overlayInnerStyle={{
width: 180,
padding: 0
}}
title={<FileParts fileList={parts}></FileParts>}
>
<Tag
className="tag-item"
color="purple"
style={{
marginRight: 0
}}
>
<span style={{ opacity: 1 }}>
<InfoCircleOutlined className="m-r-5" />
{parts.length} parts
</span>
</Tag>
</Tooltip>
);
};
const ModelFileItem: React.FC<ModelFileItemProps> = (props) => {
const {
data: item,
isEvaluating,
active,
handleSelectModelFile,
handleOnEnter
} = props;
const getModelQuantizationType = (item: any) => {
let path = item.path;
if (item?.parts?.length) {
path = `${item.path}.gguf`;
}
const quanType = getFileType(path);
if (quanType) {
return (
<Tag
className="tag-item"
color="cyan"
style={{
marginRight: 0
}}
>
{_.toUpper(quanType)}
</Tag>
);
}
return null;
};
return (
<div
className={classNames('hf-model-file', {
active: active
})}
tabIndex={0}
onClick={() => handleSelectModelFile(item)}
onKeyDown={(e) => handleOnEnter(e, item)}
>
<div className="title">{item.path}</div>
<div className="tags flex-between">
<span className="flex-center gap-8">
<Tag
className="tag-item"
color="green"
style={{
marginRight: 0
}}
>
{convertFileSize(item.size)}
</Tag>
{getModelQuantizationType(item)}
<FilePartsTag parts={item.parts}></FilePartsTag>
</span>
<IncompatiableInfo
isEvaluating={isEvaluating}
data={item.evaluateResult}
></IncompatiableInfo>
</div>
</div>
);
};
export default ModelFileItem;

@ -14,9 +14,10 @@ const SearchInputWrapper = styled.div`
const Holder = styled.div`
pointer-events: none;
position: absolute;
top: 12px;
top: 13px;
left: 34px;
color: var(--ant-color-text-quaternary);
font-size: var(--font-size-small);
z-index: 10;
kbd {
border: 1px solid var(--ant-color-border);

@ -24,7 +24,7 @@ import {
backendOptionsMap,
modelSourceMap
} from '../config';
import { identifyModelTask } from '../config/audio-catalog';
import { handleRecognizeAudioModel } from '../config/audio-catalog';
import SearchStyle from '../style/search-result.less';
import SearchInput from './search-input';
import SearchResult from './search-result';
@ -206,9 +206,9 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
}
try {
const repoList = list.map((item) => {
const isAuido = identifyModelTask(modelSource, item.name);
const res = handleRecognizeAudioModel(item, modelSource);
return {
...(isAuido ? { backend: backendOptionsMap.voxBox } : {}),
...(res.isAudio ? { backend: backendOptionsMap.voxBox } : {}),
source: modelSource,
...(modelSource === modelSourceMap.huggingface_value
? {

@ -1,4 +1,9 @@
import { modelSourceMap, modelTaskMap } from './index';
import {
HuggingFaceTaskMap,
ModelscopeTaskMap,
modelSourceMap,
modelTaskMap
} from './index';
export const HuggingFaceModels = [
{
@ -208,3 +213,32 @@ export const identifyModelTask = (source: string, modelName: string) => {
}
return '';
};
export const handleRecognizeAudioModel = (selectModel: any, source: string) => {
const modelTaskType = identifyModelTask(source, selectModel.name);
let isAudio = modelTaskType === modelTaskMap.audio;
// Check if the model is audio type, if not, check if the task is audio
if (!isAudio) {
const modelTask =
HuggingFaceTaskMap.audio.includes(selectModel.task) ||
ModelscopeTaskMap.audio.includes(selectModel.task)
? modelTaskMap.audio
: '';
isAudio = modelTask === modelTaskMap.audio;
}
const modelTaskData = {
value: selectModel.task,
type: isAudio ? modelTaskMap.audio : '',
isAudio: isAudio,
text2speech:
HuggingFaceTaskMap[modelTaskMap.textToSpeech] === selectModel.task ||
ModelscopeTaskMap[modelTaskMap.textToSpeech] === selectModel.task,
speech2text:
HuggingFaceTaskMap[modelTaskMap.speechToText] === selectModel.task ||
ModelscopeTaskMap[modelTaskMap.speechToText] === selectModel.task
};
return modelTaskData;
};

@ -8,15 +8,13 @@ import _ from 'lodash';
import { useEffect, useRef, useState } from 'react';
import { evaluationsModelSpec, queryGPUList } from '../apis';
import {
HuggingFaceTaskMap,
ModelscopeTaskMap,
backendOptionsMap,
getSourceRepoConfigValue,
modelSourceMap,
modelTaskMap,
setSourceRepoConfigValue
} from '../config';
import { identifyModelTask } from '../config/audio-catalog';
import { handleRecognizeAudioModel } from '../config/audio-catalog';
import {
EvaluateResult,
FormData,
@ -310,10 +308,12 @@ export const useCheckCompatibility = () => {
const vram = convertFileSize(resource_claim.vram, 2);
msgData = {
title: intl.formatMessage({ id: 'models.form.check.passed' }),
message: intl.formatMessage(
{ id: 'models.form.check.claims' },
{ ram, vram }
)
message: ram
? intl.formatMessage(
{ id: 'models.form.check.claims' },
{ ram, vram }
)
: intl.formatMessage({ id: 'models.form.check.claims2' }, { vram })
};
}
@ -522,28 +522,6 @@ export const useCheckCompatibility = () => {
};
export const useSelectModel = () => {
const handleRecognizeAudioModel = (selectModel: any, source: string) => {
const modelTaskType = identifyModelTask(source, selectModel.name);
const modelTask =
HuggingFaceTaskMap.audio.includes(selectModel.task) ||
ModelscopeTaskMap.audio.includes(selectModel.task)
? modelTaskMap.audio
: '';
const modelTaskData = {
value: selectModel.task,
type: modelTaskType || modelTask,
text2speech:
HuggingFaceTaskMap[modelTaskMap.textToSpeech] === selectModel.task ||
ModelscopeTaskMap[modelTaskMap.textToSpeech] === selectModel.task,
speech2text:
HuggingFaceTaskMap[modelTaskMap.speechToText] === selectModel.task ||
ModelscopeTaskMap[modelTaskMap.speechToText] === selectModel.task
};
return modelTaskData;
};
// just for setting the model name or repo_id, and the backend, Since the model type is fixed.
const onSelectModel = (selectModel: any, source: string) => {
let name = _.split(selectModel.name, '/').slice(-1)[0];
@ -551,6 +529,7 @@ export const useSelectModel = () => {
name = _.toLower(name).replace(reg, '');
const modelTaskData = handleRecognizeAudioModel(selectModel, source);
return {
repo_id: selectModel.name,
name: name,

@ -151,7 +151,7 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = forwardRef(
<div>
{
<>
<h3 className="m-b-20 m-l-10 font-size-14 line-24 font-600">
<h3 className="m-b-20 font-size-14 line-24 font-600">
{parametersTitle || (
<span>
{intl.formatMessage({ id: 'playground.parameters' })}

@ -25,6 +25,7 @@
position: relative;
padding: 0 4px;
border-radius: var(--border-radius-mini);
font-weight: var(--font-weight-medium);
}
.actions {

@ -25,8 +25,8 @@ export const convertFileSize = (
sizeInBytes?: number,
prec = 1,
allowEmpty = false
): string => {
if (!sizeInBytes) return allowEmpty ? '' : '0';
): string | number => {
if (!sizeInBytes) return allowEmpty ? '' : 0;
const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB'];
let size = sizeInBytes;

Loading…
Cancel
Save