chore: audio model deploy
This commit is contained in:
@@ -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) => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user