chore: audio model deploy

This commit is contained in:
jialin
2024-11-27 11:04:31 +08:00
parent 5e0772fae9
commit ba76498acc
31 changed files with 708 additions and 241 deletions
+31 -8
View File
@@ -147,17 +147,24 @@ export async function queryModelScopeModels(
Target?: string;
SingleCriterion?: any[];
Name: string;
filterGGUF?: boolean;
tags?: string[];
tasks?: string[];
},
config?: any
) {
const Criterion = params.filterGGUF
? {
Criterion: [
{ category: 'tags', predicate: 'contains', values: ['gguf'] }
]
}
: {};
const tagsCriterion = params.tags?.map((tag: string) => {
return { category: 'tags', predicate: 'contains', values: [tag] };
});
const tasksCriterion = params.tasks?.map((task: string) => {
return { category: 'tasks', predicate: 'contains', values: [task] };
});
const Criterion =
tagsCriterion?.length || tasksCriterion?.length
? {
Criterion: [...(tagsCriterion || []), ...(tasksCriterion || [])]
}
: {};
const res = await fetch(`${MODEL_SCOPE_LIST_MODEL_API}`, {
method: 'PUT',
signal: config?.signal,
@@ -299,3 +306,19 @@ export async function downloadModelFile(
)?.text();
return res;
}
export async function downloadModelScopeModelfile(
params: { name: string },
options?: any
) {
const res = await fetch(
`${MODE_SCOPE_MODEL_FIELS_API}${params.name}/resolve/master/config.json`,
{
method: 'GET',
signal: options?.signal
}
);
if (!res.ok) {
throw new Error('Network response was not ok');
}
return res.json();
}
@@ -35,6 +35,7 @@ interface AdvanceConfigProps {
gpuOptions: Array<any>;
action: PageActionType;
source: string;
modelTask: string;
}
const AdvanceConfig: React.FC<AdvanceConfigProps> = (props) => {
+41 -3
View File
@@ -16,8 +16,11 @@ import React, {
} from 'react';
import { queryGPUList } from '../apis';
import {
HuggingFaceTaskMap,
ModelscopeTaskMap,
backendOptionsMap,
modelSourceMap,
modelTaskMap,
ollamaModelOptions
} from '../config';
import { FormData, GPUListItem } from '../config/types';
@@ -45,6 +48,12 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
const [gpuOptions, setGpuOptions] = useState<
Array<GPUListItem & { label: string; value: string }>
>([]);
const [modelTask, setModelTask] = useState<Record<string, any>>({
type: '',
value: '',
text2speech: false,
speech2text: false
});
const sourceOptions = [
{
@@ -116,6 +125,27 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
const reg = /(-gguf)$/i;
name = _.toLower(name).replace(reg, '');
const modelTask =
HuggingFaceTaskMap.audio.includes(props.selectedModel.task) ||
ModelscopeTaskMap.audio.includes(props.selectedModel.task)
? modelTaskMap.audio
: '';
setModelTask({
value: props.selectedModel.task,
type: modelTask,
text2speech:
HuggingFaceTaskMap[modelTaskMap.textToSpeech] ===
props.selectedModel.task ||
ModelscopeTaskMap[modelTaskMap.textToSpeech] ===
props.selectedModel.task,
speech2text:
HuggingFaceTaskMap[modelTaskMap.speechToText] ===
props.selectedModel.task ||
ModelscopeTaskMap[modelTaskMap.speechToText] ===
props.selectedModel.task
});
if (SEARCH_SOURCE.includes(props.source)) {
form.setFieldsValue({
repo_id: props.selectedModel.name,
@@ -321,6 +351,8 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
if (gpu) {
onOk({
..._.omit(formdata, ['scheduleType']),
speech_to_text: modelTask.speech2text,
text_to_speech: modelTask.text2speech,
gpu_selector: {
gpu_name: gpu.name,
gpu_index: gpu.index,
@@ -329,19 +361,24 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
});
} else {
onOk({
..._.omit(formdata, ['scheduleType'])
..._.omit(formdata, ['scheduleType']),
speech_to_text: modelTask.speech2text,
text_to_speech: modelTask.text2speech
});
}
};
useEffect(() => {
if (action === PageAction.CREATE) {
if (action === PageAction.EDIT) return;
if (modelTask.type === modelTaskMap.audio) {
form.setFieldValue('backend', backendOptionsMap.voxBox);
} else {
form.setFieldValue(
'backend',
isGGUF ? backendOptionsMap.llamaBox : backendOptionsMap.vllm
);
}
}, [isGGUF]);
}, [isGGUF, modelTask]);
useEffect(() => {
handleOnSelectModel();
}, [props.selectedModel.name]);
@@ -449,6 +486,7 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
form={form}
gpuOptions={gpuOptions}
isGGUF={isGGUF}
modelTask={modelTask}
action={action}
source={props.source}
></AdvanceConfig>
@@ -23,7 +23,7 @@ interface HFModelItemProps {
source?: string;
tags?: string[];
}
const warningTask = ['audio', 'video'];
const warningTask = ['video'];
const SUPPORTEDSOURCE = [
modelSourceMap.huggingface_value,
+96 -6
View File
@@ -10,11 +10,18 @@ import { useIntl } from '@umijs/max';
import { Button, Empty, Spin, Tag, Tooltip } from 'antd';
import { some } from 'lodash';
import 'overlayscrollbars/overlayscrollbars.css';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState
} from 'react';
import SimpleBar from 'simplebar-react';
import 'simplebar-react/dist/simplebar.min.css';
import {
downloadModelFile,
downloadModelScopeModelfile,
queryHuggingfaceModelDetail,
queryModelScopeModelDetail
} from '../apis';
@@ -37,10 +44,32 @@ const ModelCard: React.FC<{
const [readmeText, setReadmeText] = useState<string | null>(null);
const requestToken = useRef<any>(null);
const axiosTokenRef = useRef<any>(null);
const loadConfigTokenRef = useRef<any>(null);
const loadConfigJsonTokenRef = useRef<any>(null);
const [isGGUFModel, setIsGGUFModel] = useState<boolean>(false);
const [loading, setLoading] = useState<boolean>(false);
const loadFile = async (repo: string, sha: string) => {
const modelTags = useMemo(() => {
if (modelSource === modelSourceMap.huggingface_value) {
return modelData?.pipeline_tag ? [modelData?.pipeline_tag] : [];
}
if (modelSource === modelSourceMap.modelscope_value) {
return modelData?.Tasks?.map((task: any) => task?.Name)?.filter(
(val: string) => val
);
}
return [];
}, [modelSource, modelData]);
const modelType = useMemo(() => {
if (modelSource === modelSourceMap.huggingface_value) {
return modelData?.config?.model_type || modelData?.ModelType?.[0];
}
if (modelSource === modelSourceMap.modelscope_value) {
return modelData?.ModelType?.[0];
}
}, [modelData, modelSource]);
const loadFile = useCallback(async (repo: string, sha: string) => {
try {
axiosTokenRef.current?.abort?.();
axiosTokenRef.current = new AbortController();
@@ -54,12 +83,32 @@ const ModelCard: React.FC<{
signal: axiosTokenRef.current.signal
}
);
console.log('readme++++++++', res);
return res || '';
} catch (error) {
return '';
}
};
}, []);
const loadConfig = useCallback(async (repo: string, sha: string) => {
try {
loadConfigTokenRef.current?.abort?.();
loadConfigTokenRef.current = new AbortController();
const res = await downloadModelFile(
{
repo,
revision: sha,
path: 'config.json'
},
{
signal: loadConfigTokenRef.current.signal
}
);
return res || null;
} catch (error) {
console.log('error======', error);
return null;
}
}, []);
const removeMetadata = useCallback((str: string) => {
let indexes = [];
@@ -81,6 +130,12 @@ const ModelCard: React.FC<{
// huggingface model card data
const getHuggingfaceModelDetail = async () => {
try {
const configjson = await loadConfig(
props.selectedModel.name,
'main'
).catch(() => {
return null;
});
const [modelcard, readme] = await Promise.all([
queryHuggingfaceModelDetail(
{ repo: props.selectedModel.name },
@@ -92,11 +147,13 @@ const ModelCard: React.FC<{
]);
setModelData(modelcard);
// remove the meta data from readme
const newReadme = removeMetadata(readme);
setReadmeText(newReadme);
const isGGUF = modelcard.tags?.includes('gguf');
console.log('modelData++++++++++++', isGGUF);
setIsGGUF(isGGUF);
setIsGGUFModel(isGGUF);
} catch (error) {
@@ -107,8 +164,30 @@ const ModelCard: React.FC<{
}
};
const loadModelscopeModelConfig = useCallback(async (name: string) => {
try {
loadConfigJsonTokenRef.current?.abort?.();
loadConfigJsonTokenRef.current = new AbortController();
return await downloadModelScopeModelfile(
{
name: name
},
{
signal: loadConfigJsonTokenRef.current.token
}
);
} catch (error) {
return null;
}
}, []);
const getModelScopeModelDetail = async () => {
try {
const configjson = await loadModelscopeModelConfig(
props.selectedModel.name
).catch(() => {
return null;
});
const data = await queryModelScopeModelDetail(
{
name: props.selectedModel.name
@@ -121,6 +200,7 @@ const ModelCard: React.FC<{
...data?.Data,
name: `${data.Data?.Path}/${data.Data?.Name}`
});
console.log('modelData++++++++++++', configjson, data?.Data);
setReadmeText(data?.Data?.ReadMeContent);
const isGGUF = some(
data?.Data?.Tags,
@@ -230,6 +310,8 @@ const ModelCard: React.FC<{
return () => {
requestToken.current?.cancel?.();
axiosTokenRef.current?.abort?.();
loadConfigTokenRef.current?.abort?.();
loadConfigJsonTokenRef.current?.abort?.();
};
}, []);
@@ -243,13 +325,13 @@ const ModelCard: React.FC<{
{modelData ? (
<div className="model-card-wrap">
<div className="flex-center">
{modelData.config?.model_type && (
{modelType && (
<Tag className="tag-item" color="gold">
<span style={{ opacity: 0.65 }}>
<span className="m-r-5">
{intl.formatMessage({ id: 'models.architecture' })}:
</span>
{modelData.config?.model_type}
{modelType}
</span>
</Tag>
)}
@@ -258,6 +340,14 @@ const ModelCard: React.FC<{
<span style={{ opacity: 0.65 }}>GGUF</span>
</Tag>
)}
{!!modelTags.length &&
modelTags.map((tag: string, index: number) => {
return (
<Tag className="tag-item" color="geekblue" key={index}>
<span style={{ opacity: 0.65 }}>{tag}</span>
</Tag>
);
})}
</div>
{readmeText && isGGUFModel && (
<div
+62 -22
View File
@@ -5,9 +5,12 @@ import _ from 'lodash';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { queryHuggingfaceModels, queryModelScopeModels } from '../apis';
import {
HuggingFaceTaskMap,
ModelScopeSortType,
ModelSortType,
ModelscopeTaskMap,
modelSourceMap,
modelTaskMap,
ollamaModelOptions
} from '../config';
import SearchStyle from '../style/search-result.less';
@@ -23,7 +26,7 @@ interface SearchInputProps {
const SearchModel: React.FC<SearchInputProps> = (props) => {
const intl = useIntl();
const { modelSource, setLoadingModel, onSourceChange, onSelectModel } = props;
const { modelSource, setLoadingModel, onSelectModel } = props;
const [dataSource, setDataSource] = useState<{
repoOptions: any[];
loading: boolean;
@@ -44,6 +47,7 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
const axiosTokenRef = useRef<any>(null);
const searchInputRef = useRef<any>('');
const filterGGUFRef = useRef<boolean | undefined>();
const filterTaskRef = useRef<string>('');
const modelFilesSortOptions = useRef<any[]>([
{
label: intl.formatMessage({ id: 'models.sort.trending' }),
@@ -64,7 +68,6 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
]);
const handleOnSelectModel = useCallback((item: any) => {
console.log('handleOnSelectModel', item);
onSelectModel(item);
setCurrent(item.id);
}, []);
@@ -78,7 +81,7 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
query: searchInputRef.current || '',
sort: sort,
tags: filterGGUFRef.current ? ['gguf'] : [],
task
task: HuggingFaceTaskMap[filterTaskRef.current] || task
}
};
const data = await queryHuggingfaceModels(params, {
@@ -102,7 +105,10 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
try {
const params = {
Name: `${searchInputRef.current}`,
filterGGUF: filterGGUFRef.current,
tags: filterGGUFRef.current ? ['gguf'] : [],
tasks: filterTaskRef.current
? ([ModelscopeTaskMap[filterTaskRef.current]] as string[])
: [],
SortBy: ModelScopeSortType[sort]
};
const data = await queryModelScopeModels(params, {
@@ -213,6 +219,11 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
handleOnSearchRepo();
};
const handleFilterTaskChange = useCallback((value: string) => {
filterTaskRef.current = value;
handleOnSearchRepo();
}, []);
const renderHFSearch = () => {
return (
<>
@@ -222,15 +233,22 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
modelSource={modelSource}
></SearchInput>
<div className={SearchStyle.filter}>
<span>
{/* <span>
<span className="value">
{intl.formatMessage(
{ id: 'models.search.result' },
{ count: dataSource.repoOptions.length }
)}
</span>
</span>
<span>
</span> */}
<span
style={{
flex: 1,
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center'
}}
>
<Checkbox
onChange={handleFilterGGUFChange}
className="m-r-5"
@@ -253,21 +271,43 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
<InfoCircleOutlined className="m-l-4" />
</Tooltip>
</Checkbox>
<Select
allowClear
value={dataSource.sortType}
onChange={handleSortChange}
labelRender={({ label }) => {
return (
<span>
{intl.formatMessage({ id: 'model.deploy.sort' })}: {label}
</span>
);
}}
options={modelFilesSortOptions.current}
size="middle"
style={{ width: '150px' }}
></Select>
<span className="flex gap-6">
<Select
allowClear
value={filterTaskRef.current}
onChange={handleFilterTaskChange}
options={[
{
label: intl.formatMessage({
id: 'playground.audio.texttospeech'
}),
value: modelTaskMap.textToSpeech
},
{
label: intl.formatMessage({
id: 'playground.audio.speechtotext'
}),
value: modelTaskMap.speechToText
}
]}
size="middle"
style={{ width: '140px' }}
></Select>
<Select
value={dataSource.sortType}
onChange={handleSortChange}
labelRender={({ label }) => {
return (
<span>
{intl.formatMessage({ id: 'model.deploy.sort' })}: {label}
</span>
);
}}
options={modelFilesSortOptions.current}
size="middle"
style={{ width: '140px' }}
></Select>
</span>
</span>
</div>
</>
+32
View File
@@ -73,6 +73,38 @@ export const backendOptionsMap = {
voxBox: 'vox-box'
};
export const modelTaskMap = {
textToSpeech: 'text-to-speech',
speechToText: 'speech-to-text',
textToText: 'text-to-text',
textToImage: 'text-to-image',
audio: 'audio',
image: 'image'
};
export const ModelscopeTaskMap = {
[modelTaskMap.textToSpeech]: 'text-to-speech',
[modelTaskMap.speechToText]: 'auto-speech-recognition',
[modelTaskMap.textToText]: 'TextToText',
[modelTaskMap.textToImage]: 'text-to-image',
audio: ['text-to-speech', 'auto-speech-recognition']
};
export const HuggingFaceTaskMap = {
[modelTaskMap.textToSpeech]: 'text-to-speech',
[modelTaskMap.speechToText]: 'automatic-speech-recognition',
[modelTaskMap.textToText]: 'text-2-text',
[modelTaskMap.textToImage]: 'text-to-image',
audio: ['text-to-speech', 'automatic-speech-recognition']
};
export const AudioModeTypeMap = {
FunASR: ['FunASR', 'funasr', 'fun-asr', 'fun_asr'],
Bark: ['Bark', 'bark'],
Whisper: ['Whisper', 'whisper'],
CosyVoice: ['CosyVoice', 'cosyvoice', 'cosy-voice', 'cosy_voice']
};
export const modelSourceMap: Record<string, string> = {
huggingface: 'Hugging Face',
ollama_library: 'Ollama Library',