diff --git a/config/proxy.ts b/config/proxy.ts index 6beb12ac..6c09cb91 100644 --- a/config/proxy.ts +++ b/config/proxy.ts @@ -20,8 +20,9 @@ export default function createProxyTable(target?: string) { ws: true, pathRewrite: (pth: string) => pth.replace(`/^/${api}`, `/${api}`), // onProxyRes: (proxyRes: any, req: any, res: any) => { + // console.log('proxyRes=====', req); + // proxyRes.on('data', (chunk: any) => { - // console.log('chunk=====', chunk); // res.write(chunk); // }); @@ -30,7 +31,6 @@ export default function createProxyTable(target?: string) { // }); // proxyRes.on('error', (err: any) => { - // console.error('Proxy stream error:', err); // res.status(500).end('Stream error'); // }); // }, diff --git a/src/components/seal-form/config/components.ts b/src/components/seal-form/config/components.ts new file mode 100644 index 00000000..cee9eb70 --- /dev/null +++ b/src/components/seal-form/config/components.ts @@ -0,0 +1,19 @@ +import { Slider } from 'antd'; +import SealInput from '../seal-input'; +import SealSelect from '../seal-select'; + +const components: { + InputNumber: typeof SealInput.Number; + Select: typeof SealSelect; + Slider: React.ComponentType; + TextArea: typeof SealInput.TextArea; + Input: typeof SealInput.Input; +} = { + InputNumber: SealInput.Number, + Select: SealSelect, + Slider: Slider as React.ComponentType, + TextArea: SealInput.TextArea, + Input: SealInput.Input +}; + +export default components; diff --git a/src/components/seal-form/field-component.tsx b/src/components/seal-form/field-component.tsx new file mode 100644 index 00000000..bd316fb0 --- /dev/null +++ b/src/components/seal-form/field-component.tsx @@ -0,0 +1,19 @@ +import { ParamsSchema } from '@/pages/playground/config/types'; +import { useIntl } from '@umijs/max'; +import React from 'react'; +import componentsMap from './config/components'; + +const FieldComponent: React.FC = (props) => { + const intl = useIntl(); + const { type, label, attrs, style, ...rest } = props; + return React.createElement(componentsMap[type], { + ...rest, + ...attrs, + style: { ...style, width: '100%' }, + label: label.isLocalized + ? intl.formatMessage({ id: label.text }) + : label.text + }); +}; + +export default React.memo(FieldComponent); diff --git a/src/locales/en-US/playground.ts b/src/locales/en-US/playground.ts index 81e28763..a6b4c976 100644 --- a/src/locales/en-US/playground.ts +++ b/src/locales/en-US/playground.ts @@ -75,7 +75,7 @@ export default { 'playground.rerank.rank': 'Rank', 'playground.rerank.score': 'Score', 'playground.rerank.query.holder': 'Input your query', - 'playground.image.prompt': 'Input Prompt', + 'playground.image.prompt': 'Text Prompt', 'playground.audio.texttospeech': 'Text to Speech', 'playground.audio.speechtotext': 'Speech to Text', 'playground.audio.texttospeech.tips': 'Generated speech will appear here', @@ -90,5 +90,13 @@ export default { 'Please upload an audio file, supported formats: {formats}', 'playground.input.multiplePaste': 'Multi-line paste', 'playground.multiple.on': 'Enable', - 'playground.multiple.off': 'Disable' + 'playground.multiple.off': 'Disable', + 'playground.image.params.sampler': 'Sampler', + 'playground.image.params.samplerSteps': 'Sampler Steps', + 'playground.image.params.seed': 'Seed', + 'playground.image.params.negativePrompt': 'Negative Prompt', + 'playground.image.params.cfgScale': 'Scale Factor', + 'playground.image.params.custom': 'Custom', + 'playground.image.params.custom.tips': 'Parameter definition', + 'playground.image.params.openai': 'OpenAI Compatible' }; diff --git a/src/locales/zh-CN/playground.ts b/src/locales/zh-CN/playground.ts index 56efb68e..ec1b6581 100644 --- a/src/locales/zh-CN/playground.ts +++ b/src/locales/zh-CN/playground.ts @@ -88,5 +88,13 @@ export default { 'playground.audio.button.generate': '生成文本', 'playground.input.multiplePaste': '多行粘贴', 'playground.multiple.on': '开启', - 'playground.multiple.off': '关闭' + 'playground.multiple.off': '关闭', + 'playground.image.params.sampler': '采样器', + 'playground.image.params.samplerSteps': '采样器步数', + 'playground.image.params.seed': '随机种子', + 'playground.image.params.negativePrompt': '负面提示', + 'playground.image.params.cfgScale': '缩放因子', + 'playground.image.params.custom': '自定义', + 'playground.image.params.custom.tips': '参数定义', + 'playground.image.params.openai': 'OpenAI 兼容' }; diff --git a/src/pages/llmodels/apis/index.ts b/src/pages/llmodels/apis/index.ts index 3df8158e..78f69b34 100644 --- a/src/pages/llmodels/apis/index.ts +++ b/src/pages/llmodels/apis/index.ts @@ -117,16 +117,10 @@ export async function queryModelInstanceLogs(id: number) { // ===================== call huggingface quicksearch api ===================== -const HUGGINGFACE_API = '/proxy?url=https://huggingface.co/api/models'; - const MODEL_SCOPE_LIST_MODEL_API = - '/proxy?url=https://www.modelscope.cn/api/v1/dolphin/models'; + 'https://www.modelscope.cn/api/v1/dolphin/models'; -const MODEL_SCOPE_DETAIL_MODEL_API = - '/proxy?url=https://www.modelscope.cn/api/v1/dolphin/models/'; - -const MODE_SCOPE_MODEL_FIELS_API = - '/proxy?url=https://modelscope.cn/api/v1/models/'; +const MODE_SCOPE_MODEL_FIELS_API = 'https://modelscope.cn/api/v1/models/'; export async function queryHuggingfaceModelDetail( params: { repo: string }, @@ -165,7 +159,7 @@ export async function queryModelScopeModels( Criterion: [...(tagsCriterion || []), ...(tasksCriterion || [])] } : {}; - const res = await fetch(`${MODEL_SCOPE_LIST_MODEL_API}`, { + const res = await fetch(setProxyUrl(`${MODEL_SCOPE_LIST_MODEL_API}`), { method: 'PUT', signal: config?.signal, headers: { @@ -190,7 +184,7 @@ export async function queryModelScopeModelDetail( params: { name: string }, options?: any ) { - return request(`${MODE_SCOPE_MODEL_FIELS_API}${params.name}`, { + return request(setProxyUrl(`${MODE_SCOPE_MODEL_FIELS_API}${params.name}`), { method: 'GET', cancelToken: options?.token }); @@ -200,18 +194,18 @@ export async function queryModelScopeModelFiles( params: { name: string; revision: string }, options?: any ) { - const res = await fetch( - `${MODE_SCOPE_MODEL_FIELS_API}${params.name}/repo/files?${qs.stringify({ + const url = `${MODE_SCOPE_MODEL_FIELS_API}${params.name}/repo/files?${qs.stringify( + { Revision: params.revision, Recursive: true, Root: '' - })}`, - { - method: 'GET', - signal: options?.signal, - body: null } - ); + )}`; + const res = await fetch(setProxyUrl(url), { + method: 'GET', + signal: options?.signal, + body: null + }); if (!res.ok) { throw new Error('Network response was not ok'); @@ -310,13 +304,11 @@ 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 - } - ); + const url = `${MODE_SCOPE_MODEL_FIELS_API}${params.name}/resolve/master/config.json`; + const res = await fetch(setProxyUrl(url), { + method: 'GET', + signal: options?.signal + }); if (!res.ok) { throw new Error('Network response was not ok'); } diff --git a/src/pages/llmodels/components/advance-config.tsx b/src/pages/llmodels/components/advance-config.tsx index 1ea80b06..0977f782 100644 --- a/src/pages/llmodels/components/advance-config.tsx +++ b/src/pages/llmodels/components/advance-config.tsx @@ -35,7 +35,7 @@ interface AdvanceConfigProps { gpuOptions: Array; action: PageActionType; source: string; - modelTask: string; + modelTask: Record; } const AdvanceConfig: React.FC = (props) => { diff --git a/src/pages/llmodels/components/data-form.tsx b/src/pages/llmodels/components/data-form.tsx index 6d4c84b6..341e6d48 100644 --- a/src/pages/llmodels/components/data-form.tsx +++ b/src/pages/llmodels/components/data-form.tsx @@ -23,6 +23,7 @@ import { modelTaskMap, ollamaModelOptions } from '../config'; +import { HuggingFaceModels, ModelScopeModels } from '../config/audio-catalog'; import { FormData, GPUListItem } from '../config/types'; import AdvanceConfig from './advance-config'; @@ -120,11 +121,30 @@ const DataForm: React.FC = forwardRef((props, ref) => { [] ); + const identifyModelTask = () => { + let data = null; + if (props.source === modelSourceMap.huggingface_value) { + data = HuggingFaceModels.find( + (item) => `${item.org}/${item.name}` === props.selectedModel.name + ); + } + if (props.source === modelSourceMap.modelscope_value) { + data = ModelScopeModels.find( + (item) => `${item.org}/${item.name}` === props.selectedModel.name + ); + } + if (data) { + return modelTaskMap.audio; + } + return ''; + }; const handleOnSelectModel = () => { let name = _.split(props.selectedModel.name, '/').slice(-1)[0]; const reg = /(-gguf)$/i; name = _.toLower(name).replace(reg, ''); + const modelTaskType = identifyModelTask(); + const modelTask = HuggingFaceTaskMap.audio.includes(props.selectedModel.task) || ModelscopeTaskMap.audio.includes(props.selectedModel.task) @@ -133,7 +153,7 @@ const DataForm: React.FC = forwardRef((props, ref) => { setModelTask({ value: props.selectedModel.task, - type: modelTask, + type: modelTaskType || modelTask, text2speech: HuggingFaceTaskMap[modelTaskMap.textToSpeech] === props.selectedModel.task || @@ -351,8 +371,6 @@ const DataForm: React.FC = 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, @@ -361,9 +379,7 @@ const DataForm: React.FC = forwardRef((props, ref) => { }); } else { onOk({ - ..._.omit(formdata, ['scheduleType']), - speech_to_text: modelTask.speech2text, - text_to_speech: modelTask.text2speech + ..._.omit(formdata, ['scheduleType']) }); } }; diff --git a/src/pages/llmodels/components/model-card.tsx b/src/pages/llmodels/components/model-card.tsx index 0146a680..58e15894 100644 --- a/src/pages/llmodels/components/model-card.tsx +++ b/src/pages/llmodels/components/model-card.tsx @@ -130,12 +130,6 @@ 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 }, @@ -183,11 +177,6 @@ const ModelCard: React.FC<{ const getModelScopeModelDetail = async () => { try { - const configjson = await loadModelscopeModelConfig( - props.selectedModel.name - ).catch(() => { - return null; - }); const data = await queryModelScopeModelDetail( { name: props.selectedModel.name @@ -200,7 +189,6 @@ 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, diff --git a/src/pages/llmodels/components/search-model.tsx b/src/pages/llmodels/components/search-model.tsx index bc3b37a1..8010eca8 100644 --- a/src/pages/llmodels/components/search-model.tsx +++ b/src/pages/llmodels/components/search-model.tsx @@ -10,7 +10,6 @@ import { ModelSortType, ModelscopeTaskMap, modelSourceMap, - modelTaskMap, ollamaModelOptions } from '../config'; import SearchStyle from '../style/search-result.less'; @@ -233,22 +232,15 @@ const SearchModel: React.FC = (props) => { modelSource={modelSource} >
- {/* + {intl.formatMessage( { id: 'models.search.result' }, { count: dataSource.repoOptions.length } )} - */} - + + = (props) => { - - - - +
diff --git a/src/pages/llmodels/config/audio-catalog.ts b/src/pages/llmodels/config/audio-catalog.ts new file mode 100644 index 00000000..8f0d2667 --- /dev/null +++ b/src/pages/llmodels/config/audio-catalog.ts @@ -0,0 +1,105 @@ +export const HuggingFaceModels = [ + { + type: 'stt', + org: 'funasr', + name: 'paraformer-zh' + }, + { + type: 'stt', + org: 'funasr', + name: 'paraformer-zh-streaming' + }, + { + type: 'stt', + org: 'funasr', + name: 'paraformer-en' + }, + { + type: 'stt', + org: 'funasr', + name: 'conformer-en' + }, + { + type: 'stt', + org: 'Qwen', + name: 'Qwen-Audio' + }, + { + type: 'stt', + org: 'Qwen', + name: 'Qwen-Audio-Chat' + }, + { + type: 'stt', + org: 'FunAudioLLM', + name: 'SenseVoiceSmall' + }, + { + type: 'stt', + org: 'Systran', + name: '*' + }, + { + type: 'tts', + org: 'suno', + name: 'bark' + }, + { + type: 'tts', + org: 'suno', + name: 'bark-small' + }, + { + type: 'tts', + org: 'FunAudioLLM', + name: 'CosyVoice-300M-Instruct' + }, + { + type: 'tts', + org: 'FunAudioLLM', + name: 'CosyVoice-300M-SFT' + }, + { + type: 'tts', + org: 'FunAudioLLM', + name: 'CosyVoice-300M' + } +]; + +export const ModelScopeModels = [ + { + type: 'stt', + org: 'iic', + name: 'SenseVoiceSmall' + }, + { + type: 'stt', + org: 'iic', + name: 'Whisper-large-v3' + }, + { + type: 'stt', + org: 'iic', + name: 'Whisper-large-v3-turbo' + }, + { + type: 'tts', + org: 'iic', + name: 'CosyVoice-300M-Instruct' + }, + { + type: 'tts', + org: 'iic', + name: 'CosyVoice-300M' + }, + { + type: 'tts', + org: 'iic', + name: 'CosyVoice-300M-25Hz' + }, + { + type: 'tts', + org: 'iic', + name: 'CosyVoice-300M-SFT' + } +]; diff --git a/src/pages/playground/components/dynamic-params.tsx b/src/pages/playground/components/dynamic-params.tsx index a1148c4e..6cc273eb 100644 --- a/src/pages/playground/components/dynamic-params.tsx +++ b/src/pages/playground/components/dynamic-params.tsx @@ -25,6 +25,7 @@ type ParamsSettingsFormProps = { type ParamsSettingsProps = { ref?: any; + parametersTitle?: React.ReactNode; selectedModel?: string; showModelSelector?: boolean; params?: Record; @@ -45,6 +46,7 @@ const ParamsSettings: React.FC = forwardRef( setParams, onValuesChange, onModelChange, + parametersTitle, selectedModel, globalParams, initialValues, @@ -169,7 +171,6 @@ const ParamsSettings: React.FC = forwardRef( ); const renderFields = useMemo(() => { - console.log('paramsConfig:', paramsConfig); if (!paramsConfig?.length) { return null; } @@ -262,9 +263,11 @@ const ParamsSettings: React.FC = forwardRef( { <>

- - {intl.formatMessage({ id: 'playground.parameters' })} - + {parametersTitle || ( + + {intl.formatMessage({ id: 'playground.parameters' })} + + )}

name="model" diff --git a/src/pages/playground/components/ground-images.tsx b/src/pages/playground/components/ground-images.tsx index e933a245..a5ab181b 100644 --- a/src/pages/playground/components/ground-images.tsx +++ b/src/pages/playground/components/ground-images.tsx @@ -1,12 +1,13 @@ import AlertInfo from '@/components/alert-info'; +import FieldComponent from '@/components/seal-form/field-component'; import SealInput from '@/components/seal-form/seal-input'; import SealSelect from '@/components/seal-form/seal-select'; import useOverlayScroller from '@/hooks/use-overlay-scroller'; import ThumbImg from '@/pages/playground/components/thumb-img'; import { fetchChunkedData, readStreamData } from '@/utils/fetch-chunk-data'; -import { FileImageOutlined } from '@ant-design/icons'; +import { FileImageOutlined, SwapOutlined } from '@ant-design/icons'; import { useIntl, useSearchParams } from '@umijs/max'; -import { Form } from 'antd'; +import { Button, Form, Tooltip } from 'antd'; import classNames from 'classnames'; import _ from 'lodash'; import 'overlayscrollbars/overlayscrollbars.css'; @@ -22,7 +23,11 @@ import React, { } from 'react'; import { CREAT_IMAGE_API } from '../apis'; import { OpenAIViewCode } from '../config'; -import { ImageParamsConfig as paramsConfig } from '../config/params-config'; +import { + ImageAdvancedParamsConfig, + ImageconstExtraConfig, + ImageParamsConfig as paramsConfig +} from '../config/params-config'; import { MessageItem, ParamsSchema } from '../config/types'; import '../style/ground-left.less'; import '../style/system-message-wrap.less'; @@ -40,53 +45,13 @@ const initialValues = { n: 1, size: '512x512', quality: 'standard', - style: '' + style: null }; -const extraConfig: ParamsSchema[] = [ - { - type: 'Select', - name: 'quality', - options: [ - { label: 'playground.params.standard', value: 'standard', locale: true }, - { label: 'playground.params.hd', value: 'hd', locale: true } - ], - label: { - text: 'playground.params.quality', - isLocalized: true - }, - rules: [ - { - required: false - } - ] - }, - { - type: 'Select', - name: 'style', - options: [ - { label: 'playground.params.style.vivid', value: 'vivid', locale: true }, - { - label: 'playground.params.style.natural', - value: 'natural', - locale: true - } - ], - label: { - text: 'playground.params.style', - isLocalized: true - }, - rules: [ - { - required: false - } - ] - } -]; - const GroundImages: React.FC = forwardRef((props, ref) => { const { modelList } = props; const messageId = useRef(0); + const [isOpenaiCompatible, setIsOpenaiCompatible] = useState(true); const [imageList, setImageList] = useState< { dataUrl: string; @@ -254,7 +219,8 @@ const GroundImages: React.FC = forwardRef((props, ref) => { const params = { stream: true, stream_options: { - chunk_result: true + chunk_result: true, + chunk_size: 16 * 1024 }, prompt: current?.content || currentPrompt || '', ..._.omitBy(finalParameters, (value: string) => !value) @@ -263,6 +229,7 @@ const GroundImages: React.FC = forwardRef((props, ref) => { const result: any = await fetchChunkedData({ data: params, url: CREAT_IMAGE_API, + // url: 'http://192.168.50.27:9090/v1/images/generations', signal: requestToken.current.signal }); @@ -324,8 +291,43 @@ const GroundImages: React.FC = forwardRef((props, ref) => { setShow(false); }; + const handleToggleParamsStyle = () => { + if (isOpenaiCompatible) { + form.current?.form?.setFieldsValue({ + seed: null, + sampler: 'euler_a', + cfg_scale: 1, + sample_steps: 5, + negative_prompt: null + }); + setParams((pre: object) => { + return { + ...pre, + seed: null, + sampler: 'euler_a', + cfg_scale: 1, + sample_steps: 5, + negative_prompt: null + }; + }); + } else { + setParams((pre: object) => { + return { + ..._.omit(pre, [ + 'seed', + 'sampler', + 'cfg_scale', + 'sample_steps', + 'negative_prompt' + ]) + }; + }); + } + setIsOpenaiCompatible(!isOpenaiCompatible); + }; + const renderExtra = useMemo(() => { - return extraConfig.map((item: ParamsSchema) => { + return ImageconstExtraConfig.map((item: ParamsSchema) => { return ( = forwardRef((props, ref) => { ); }); - }, [extraConfig, intl]); + }, [ImageconstExtraConfig, intl]); + + const renderAdvanced = useMemo(() => { + if (isOpenaiCompatible) { + return []; + } + return ImageAdvancedParamsConfig.map((item: ParamsSchema) => { + return ( + + + + ); + }); + }, [ImageAdvancedParamsConfig, isOpenaiCompatible, intl]); const renderCustomSize = useMemo(() => { if (size === 'custom') { @@ -419,7 +434,6 @@ const GroundImages: React.FC = forwardRef((props, ref) => { updateScrollerPosition(); } messageListLengthCache.current = imageList.length; - console.log('imageList:', imageList); }, [imageList.length]); return ( @@ -445,7 +459,7 @@ const GroundImages: React.FC = forwardRef((props, ref) => { dataList={imageList} loading={loading} responseable={true} - gutter={[16, 16]} + gutter={[8, 16]} autoSize={true} > {!imageList.length && ( @@ -474,7 +488,7 @@ const GroundImages: React.FC = forwardRef((props, ref) => { placeholer={intl.formatMessage({ id: 'playground.input.prompt.holder' })} - actions={[]} + actions={['clear']} loading={loading} disabled={!parameters.model} isEmpty={!imageList.length} @@ -499,13 +513,40 @@ const GroundImages: React.FC = forwardRef((props, ref) => {
+ + {intl.formatMessage({ id: 'playground.parameters' })} + + + + +
+ } setParams={setParams} paramsConfig={paramsConfig} initialValues={initialValues} params={parameters} selectedModel={selectModel} modelList={modelList} - extra={[renderCustomSize, ...renderExtra]} + extra={[renderCustomSize, ...renderExtra, ...renderAdvanced]} /> diff --git a/src/pages/playground/components/message-input.tsx b/src/pages/playground/components/message-input.tsx index 23406574..e2e06128 100644 --- a/src/pages/playground/components/message-input.tsx +++ b/src/pages/playground/components/message-input.tsx @@ -2,7 +2,7 @@ import IconFont from '@/components/icon-font'; import HotKeys, { KeyMap } from '@/config/hotkeys'; import { ClearOutlined, SendOutlined, SwapOutlined } from '@ant-design/icons'; import { useIntl } from '@umijs/max'; -import { Button, Checkbox, Divider, Input, Select, Tooltip } from 'antd'; +import { Button, Checkbox, Divider, Input, Tooltip } from 'antd'; import _ from 'lodash'; import React, { useCallback, useMemo, useRef, useState } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; @@ -64,7 +64,6 @@ const layoutOptions = [ ]; interface MessageInputProps { - modelList?: Global.BaseOption[]; handleSubmit: (params: CurrentMessage) => void; handleAbortFetch: () => void; updateLayout?: (value: { span: number; count: number }) => void; @@ -80,7 +79,6 @@ interface MessageInputProps { addMessage?: (message: CurrentMessage) => void; tools?: React.ReactNode; loading: boolean; - showModelSelection?: boolean; disabled: boolean; isEmpty?: boolean; placeholer?: string; @@ -94,15 +92,12 @@ interface MessageInputProps { const MessageInput: React.FC = ({ handleSubmit, handleAbortFetch, - setModelSelections, presetPrompt, clearAll, updateLayout, addMessage, onCheck, loading, - modelList, - showModelSelection, disabled, isEmpty, submitIcon, @@ -170,21 +165,11 @@ const MessageInput: React.FC = ({ const handleClearAll = (e: any) => { e.stopPropagation(); clearAll(); - }; - - const handleUpdateModelSelections = (value: string[]) => { - const list = value?.map?.((val) => { - return { - value: val, - label: val, - instanceId: Symbol(val) - }; + setMessage({ + role: Roles.User, + content: '', + imgs: [] }); - setModelSelections?.(list); - }; - - const handleOpenPrompt = () => { - setOpen(true); }; const handleAddMessage = (e?: any) => { @@ -379,9 +364,7 @@ const MessageInput: React.FC = ({ )} {actions.includes('clear') && ( - +