chore: upload a ref audio

This commit is contained in:
jialin
2026-01-31 14:06:50 +08:00
parent f278716b7e
commit 7a5c28228b
12 changed files with 340 additions and 245 deletions
+1 -1
View File
@@ -47,7 +47,7 @@ const UploadAudio: React.FC<UploadAudioProps> = (props) => {
return (
<Tooltip
styles={{
body: {
container: {
maxWidth: 290,
width: 'max-content'
}
+5 -3
View File
@@ -165,7 +165,9 @@ export default {
'playground.params.duration': 'Duration (seconds)',
'playground.params.resolution': 'Resolution',
'playground.params.taskType': 'Task Type',
'playground.params.voiceStyle': 'Voice Style',
'playground.params.maxTokens': 'Maximum Generate Length',
'playground.params.refAudio': 'Reference Audio URL'
'playground.params.voiceStyle': 'Instructions',
'playground.params.maxTokens': 'Max New Tokens',
'playground.params.refAudio': 'Reference Audio',
'playground.params.refAudio.tips':
'Enter a reference audio URL, or upload an audio file.'
};
+5 -3
View File
@@ -168,9 +168,11 @@ export default {
'playground.params.duration': 'Duration (seconds)',
'playground.params.resolution': 'Resolution',
'playground.params.taskType': 'Task Type',
'playground.params.voiceStyle': 'Voice Style',
'playground.params.maxTokens': 'Maximum Generate Length',
'playground.params.refAudio': 'Reference Audio URL'
'playground.params.voiceStyle': 'Instructions',
'playground.params.maxTokens': 'Max New Tokens',
'playground.params.refAudio': 'Reference Audio',
'playground.params.refAudio.tips':
'Enter a reference audio URL, or upload an audio file.'
};
// ========== To-Do: Translate Keys (Remove After Translation) ==========
+5 -3
View File
@@ -162,9 +162,11 @@ export default {
'playground.params.duration': 'Duration (seconds)',
'playground.params.resolution': 'Resolution',
'playground.params.taskType': 'Task Type',
'playground.params.voiceStyle': 'Voice Style',
'playground.params.maxTokens': 'Maximum Generate Length',
'playground.params.refAudio': 'Reference Audio URL'
'playground.params.voiceStyle': 'Instructions',
'playground.params.maxTokens': 'Max New Tokens',
'playground.params.refAudio': 'Reference Audio',
'playground.params.refAudio.tips':
'Enter a reference audio URL, or upload an audio file.'
};
// ========== To-Do: Translate Keys (Remove After Translation) ==========
+5 -3
View File
@@ -158,7 +158,9 @@ export default {
'playground.params.duration': '时长 (秒)',
'playground.params.resolution': '分辨率',
'playground.params.taskType': '任务类型',
'playground.params.voiceStyle': '语音风格',
'playground.params.maxTokens': '最大生成长度',
'playground.params.refAudio': '参考音频 URL'
'playground.params.voiceStyle': '情感',
'playground.params.maxTokens': '最大生成 Token 数',
'playground.params.refAudio': '参考音频',
'playground.params.refAudio.tips':
'输入一个参考音频的 URL,或上传一个音频文件。'
};
+77
View File
@@ -0,0 +1,77 @@
import SealInput from '@/components/seal-form/seal-input';
import UploadAudio from '@/components/upload-audio';
import { convertFileToBase64 } from '@/utils/load-audio-file';
import { CloseCircleFilled } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Form } from 'antd';
import React from 'react';
import styled from 'styled-components';
import { useFormContext } from '../config/form-context';
const SuffixWrapper = styled.div`
.icon {
font-size: 12px;
color: var(--ant-color-text-quaternary);
cursor: pointer;
&:hover {
color: var(--ant-color-text-tertiary);
}
}
`;
export const RefAudioFormItem: React.FC = () => {
const { meta } = useFormContext();
const form = Form.useFormInstance();
const intl = useIntl();
const [filleName, setFileName] = React.useState<string>('');
// handle upload audio file, transfer to a base64 url
const handleUploadChange = async (data: { file: any; fileList: any }) => {
const { file } = data;
const base64 = await convertFileToBase64(file);
form.setFieldsValue({
ref_audio: base64
});
setFileName(file.name);
console.log('Uploaded file base64 url:', base64);
};
const handleClear = () => {
form.setFieldsValue({
ref_audio: ''
});
setFileName('');
};
return (
<Form.Item
name="ref_audio"
getValueProps={(value) => ({ value: filleName ? filleName : value })}
>
<SealInput.Input
allowClear
readOnly={!!filleName}
suffix={
<SuffixWrapper>
{filleName ? (
<span onClick={handleClear}>
<CloseCircleFilled className="icon" />
</span>
) : null}
<UploadAudio
size="small"
type="text"
accept={['.mp3', '.mp4', '.wav', '.m4a'].join(', ')}
onChange={handleUploadChange}
></UploadAudio>
</SuffixWrapper>
}
description={intl.formatMessage({
id: 'playground.params.refAudio.tips'
})}
label={intl.formatMessage({ id: 'playground.params.refAudio' })}
></SealInput.Input>
</Form.Item>
);
};
+155
View File
@@ -0,0 +1,155 @@
import _ from 'lodash';
import { ParamsSchema } from '../config/types';
export const TTSAdvancedParamsConfig: ParamsSchema[] = [
{
type: 'Input',
name: 'task_type',
options: [],
attrs: {
allowClear: true
},
label: {
text: 'playground.params.taskType',
isLocalized: true
},
rules: [
{
required: false
}
]
},
{
type: 'AutoComplete',
name: 'language',
options: [],
initAttrs: (meta: any) => {
return {
options: _.map(meta?.languages || [], (item: string) => ({
label: item,
value: item
}))
};
},
attrs: {
allowClear: true
},
label: {
text: 'playground.params.language',
isLocalized: true
},
rules: [
{
required: false
}
]
},
{
type: 'Select',
name: 'instructions',
label: {
text: 'playground.params.voiceStyle',
isLocalized: true
},
attrs: {
allowClear: true
},
initAttrs: (meta: any) => {
return {
options: _.map(meta?.voices || [], (item: string) => ({
label: item,
value: item
}))
};
},
rules: [
{
required: false
}
]
},
{
type: 'InputNumber',
name: 'max_new_tokens',
label: {
text: 'playground.params.maxTokens',
isLocalized: true
},
attrs: {
allowClear: true,
step: 1,
min: 0
},
formItemAttrs: {
getValueProps: (value: number) => {
return {
value: value || null
};
}
},
rules: [
{
required: false
}
]
}
];
export const TTSParamsConfig: ParamsSchema[] = [
{
type: 'AutoComplete',
name: 'voice',
options: [],
label: {
text: 'playground.params.voice',
isLocalized: true
},
rules: [
{
required: false,
message: 'Voice is required'
}
]
},
{
type: 'Select',
name: 'response_format',
options: [
{ label: 'mp3', value: 'mp3' },
// { label: 'opus', value: 'opus' },
// { label: 'aac', value: 'aac' },
// { label: 'flac', value: 'flac' },
{ label: 'wav', value: 'wav' }
// { label: 'pcm', value: 'pcm' }
],
label: {
text: 'playground.params.format',
isLocalized: true
},
rules: [
{
required: false
}
]
}
// {
// type: 'Select',
// name: 'speed',
// options: [
// { label: '0.25x', value: 0.25 },
// { label: '0.5x', value: 0.5 },
// { label: '1x', value: 1 },
// { label: '2x', value: 2 },
// { label: '4x', value: 4 }
// ],
// label: {
// text: 'playground.params.speed',
// isLocalized: true
// },
// rules: [
// {
// required: false
// }
// ]
// }
];
@@ -10,6 +10,7 @@ import React, {
useId,
useImperativeHandle
} from 'react';
import { FormContext } from '../config/form-context';
import { ParamsSchema } from '../config/types';
type ParamsSettingsProps = {
@@ -24,6 +25,7 @@ type ParamsSettingsProps = {
extra?: React.ReactNode;
watchFields?: string[];
formFields?: string;
meta?: Record<string, any>;
};
const ParamsSettings: React.FC<ParamsSettingsProps> = forwardRef(
@@ -36,7 +38,8 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = forwardRef(
paramsConfig,
modelList,
showModelSelector = true,
extra
extra,
meta
},
ref
) => {
@@ -136,56 +139,60 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = forwardRef(
};
return (
<Form
name={formId}
form={form}
onValuesChange={handleValuesChange}
onFinish={handleOnFinish}
onFinishFailed={handleOnFinishFailed}
initialValues={initialValues}
>
<div>
{
<>
<h3 className="m-b-20 font-size-14 line-24 font-500">
{parametersTitle || (
<span>
{intl.formatMessage({ id: 'playground.parameters' })}
</span>
<FormContext.Provider value={{ meta }}>
<Form
name={formId}
form={form}
onValuesChange={handleValuesChange}
onFinish={handleOnFinish}
onFinishFailed={handleOnFinishFailed}
initialValues={initialValues}
>
<div>
{
<>
<h3 className="m-b-20 font-size-14 line-24 font-500">
{parametersTitle || (
<span>
{intl.formatMessage({ id: 'playground.parameters' })}
</span>
)}
</h3>
{showModelSelector && (
<Form.Item
name="model"
rules={[
{
required: true,
message: intl.formatMessage(
{
id: 'common.form.rule.select'
},
{
name: intl.formatMessage({ id: 'playground.model' })
}
)
}
]}
>
<SealSelect
description={intl.formatMessage({
id: 'playground.model.noavailable.tips2'
})}
onChange={handleOnModelChange}
showSearch={true}
options={modelList}
label={intl.formatMessage({ id: 'playground.model' })}
></SealSelect>
</Form.Item>
)}
</h3>
{showModelSelector && (
<Form.Item
name="model"
rules={[
{
required: true,
message: intl.formatMessage(
{
id: 'common.form.rule.select'
},
{ name: intl.formatMessage({ id: 'playground.model' }) }
)
}
]}
>
<SealSelect
description={intl.formatMessage({
id: 'playground.model.noavailable.tips2'
})}
onChange={handleOnModelChange}
showSearch={true}
options={modelList}
label={intl.formatMessage({ id: 'playground.model' })}
></SealSelect>
</Form.Item>
)}
</>
}
{renderFields()}
{extra}
</div>
</Form>
</>
}
{renderFields()}
{extra}
</div>
</Form>
</FormContext.Provider>
);
}
);
+10 -3
View File
@@ -23,11 +23,12 @@ import React, {
useState
} from 'react';
import { AUDIO_TEXT_TO_SPEECH_API, CHAT_API, textToSpeech } from '../apis';
import { extractErrorMessage } from '../config';
import { RefAudioFormItem } from '../audio/form';
import {
TTSParamsConfig as paramsConfig,
TTSAdvancedParamsConfig
} from '../config/params-config';
} from '../audio/params-config';
import { extractErrorMessage } from '../config';
import { MessageItem, ParamsSchema } from '../config/types';
import '../style/ground-llm.less';
import '../style/system-message-wrap.less';
@@ -342,7 +343,12 @@ const GroundTTS: React.FC<MessageProps> = forwardRef((props, ref) => {
key: 'advanced_config',
label: intl.formatMessage({ id: 'resources.form.advanced' }),
forceRender: true,
children: formItems
children: (
<>
{formItems}
<RefAudioFormItem />
</>
)
}
]}
></CollapsePanel>
@@ -471,6 +477,7 @@ const GroundTTS: React.FC<MessageProps> = forwardRef((props, ref) => {
<div className="box">
<DynamicParams
ref={formRef}
meta={modelMeta}
onValuesChange={handleOnValuesChange}
initialValues={parameters}
modelList={modelList}
@@ -0,0 +1,18 @@
import { createContext, useContext } from 'react';
export interface FormContextProps {
meta?: Record<string, any>;
}
/**
* for dynamic form config useContext
*/
export const FormContext = createContext<FormContextProps>({});
export const useFormContext = () => {
const context = useContext(FormContext);
if (!context) {
throw new Error('useFormContext must be used within a FormContextProvider');
}
return context;
};
@@ -1,4 +1,3 @@
import _ from 'lodash';
import { ParamsSchema } from './types';
export interface SizeOption {
@@ -31,184 +30,6 @@ export const imageSizeOptions: {
{ label: '1024x1024(1:1)', value: '1024x1024', width: 1024, height: 1024 }
];
export const TTSParamsConfig: ParamsSchema[] = [
{
type: 'AutoComplete',
name: 'voice',
options: [],
label: {
text: 'playground.params.voice',
isLocalized: true
},
rules: [
{
required: false,
message: 'Voice is required'
}
]
},
{
type: 'Select',
name: 'response_format',
options: [
{ label: 'mp3', value: 'mp3' },
// { label: 'opus', value: 'opus' },
// { label: 'aac', value: 'aac' },
// { label: 'flac', value: 'flac' },
{ label: 'wav', value: 'wav' }
// { label: 'pcm', value: 'pcm' }
],
label: {
text: 'playground.params.format',
isLocalized: true
},
rules: [
{
required: false
}
]
}
// {
// type: 'Select',
// name: 'speed',
// options: [
// { label: '0.25x', value: 0.25 },
// { label: '0.5x', value: 0.5 },
// { label: '1x', value: 1 },
// { label: '2x', value: 2 },
// { label: '4x', value: 4 }
// ],
// label: {
// text: 'playground.params.speed',
// isLocalized: true
// },
// rules: [
// {
// required: false
// }
// ]
// }
];
export const TTSAdvancedParamsConfig: ParamsSchema[] = [
{
type: 'Select',
name: 'task_type',
options: [],
attrs: {
allowClear: true
},
label: {
text: 'playground.params.taskType',
isLocalized: true
},
initAttrs: (meta: any) => {
return {
options: _.map(meta?.task_types || [], (item: string) => ({
label: item,
value: item
}))
};
},
rules: [
{
required: false
}
]
},
{
type: 'Select',
name: 'language',
options: [],
initAttrs: (meta: any) => {
return {
options: _.map(meta?.languages || [], (item: string) => ({
label: item,
value: item
}))
};
},
attrs: {
allowClear: true
},
label: {
text: 'playground.params.language',
isLocalized: true
},
rules: [
{
required: false
}
]
},
{
type: 'Select',
name: 'instructions',
label: {
text: 'playground.params.voiceStyle',
isLocalized: true
},
attrs: {
allowClear: true
},
initAttrs: (meta: any) => {
return {
options: _.map(meta?.voices || [], (item: string) => ({
label: item,
value: item
}))
};
},
rules: [
{
required: false
}
]
},
{
type: 'Slider',
name: 'max_new_tokens',
label: {
text: 'playground.params.maxTokens',
isLocalized: true
},
attrs: {
allowClear: true,
inputnumber: true,
step: 1,
min: 0
},
formItemAttrs: {
getValueProps: (value: number) => {
return {
value: value || null
};
}
},
rules: [
{
required: false
}
]
},
{
type: 'Input',
name: 'ref_audio',
label: {
text: 'playground.params.refAudio',
isLocalized: true
},
attrs: {
allowClear: true
},
rules: [
{
required: false
}
]
}
];
export const RealtimeParamsConfig: ParamsSchema[] = [
{
type: 'Select',
+2
View File
@@ -1,6 +1,8 @@
import { message } from 'antd';
import { convertFileSize } from './index';
// TODO: create a file.ts in utils folder to manage file related utils
export const audioTypeMap: Record<string, string> = {
'audio/wav': 'wav',
'audio/mp3': 'mp3',