Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: 配置TTS参数支持试听功能 #1402

Merged
merged 1 commit into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions apps/application/serializers/application_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -973,6 +973,16 @@ def text_to_speech(self, text, with_valid=True):
**application.tts_model_params_setting)
return model.text_to_speech(text)

def play_demo_text(self, form_data, with_valid=True):
text = '你好,这里是语音播放测试'
if with_valid:
self.is_valid(raise_exception=True)
application_id = self.data.get('application_id')
application = QuerySet(Application).filter(id=application_id).first()
if application.tts_model_enable:
model = get_model_instance_by_model_user_id(application.tts_model_id, application.user_id, **form_data)
return model.text_to_speech(text)

class ApplicationKeySerializerModel(serializers.ModelSerializer):
class Meta:
model = ApplicationApiKey
Expand Down
2 changes: 2 additions & 0 deletions apps/application/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,7 @@
name='application/audio'),
path('application/<str:application_id>/text_to_speech', views.Application.TextToSpeech.as_view(),
name='application/audio'),
path('application/<str:application_id>/play_demo_text', views.Application.PlayDemoText.as_view(),
name='application/audio'),

]
16 changes: 16 additions & 0 deletions apps/application/views/application_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -566,3 +566,19 @@ def post(self, request: Request, application_id: str):
request.data.get('text'))
return HttpResponse(byte_data, status=200, headers={'Content-Type': 'audio/mp3',
'Content-Disposition': 'attachment; filename="abc.mp3"'})

class PlayDemoText(APIView):
authentication_classes = [TokenAuth]

@action(methods=['POST'], detail=False)
@has_permissions(ViewPermission([RoleConstants.ADMIN, RoleConstants.USER, RoleConstants.APPLICATION_ACCESS_TOKEN],
[lambda r, keywords: Permission(group=Group.APPLICATION,
operate=Operate.USE,
dynamic_tag=keywords.get(
'application_id'))],
compare=CompareConstants.AND))
def post(self, request: Request, application_id: str):
byte_data = ApplicationSerializer.Operate(
data={'application_id': application_id, 'user_id': request.user.id}).play_demo_text(request.data)
return HttpResponse(byte_data, status=200, headers={'Content-Type': 'audio/mp3',
'Content-Disposition': 'attachment; filename="abc.mp3"'})
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,6 @@ def text_to_speech(self, text):
print(audio)
raise Exception(audio)
return audio

def is_cache_model(self):
return False
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,6 @@ def text_to_speech(self, text):
input=text,
) as response:
return response.read()

def is_cache_model(self):
return False
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ def text_to_speech(self, text):

return asyncio.run(self.submit(request_json, text))

def is_cache_model(self):
return False

def token_auth(self):
return {'Authorization': 'Bearer; {}'.format(self.volcanic_token)}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ async def handle():

return asyncio.run(handle())

def is_cache_model(self):
return False

@staticmethod
async def handle_message(ws):
audio_bytes: bytes = b''
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,6 @@ def text_to_speech(self, text):
input=text,
) as response:
return response.read()

def is_cache_model(self):
return False
16 changes: 14 additions & 2 deletions ui/src/api/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ const postSpeechToText: (
}

/**
* 语音转文本
* 文本转语音
*/
const postTextToSpeech: (
application_id: String,
Expand All @@ -353,6 +353,17 @@ const postTextToSpeech: (
) => Promise<Result<any>> = (application_id, data, loading) => {
return download(`${prefix}/${application_id}/text_to_speech`, 'post', data, undefined, loading)
}

/**
* 播放测试文本
*/
const playDemoText: (
application_id: String,
data: any,
loading?: Ref<boolean>
) => Promise<Result<any>> = (application_id, data, loading) => {
return download(`${prefix}/${application_id}/play_demo_text`, 'post', data, undefined, loading)
}
/**
* 获取平台状态
*/
Expand Down Expand Up @@ -430,5 +441,6 @@ export default {
getPlatformConfig,
updatePlatformConfig,
updatePlatformStatus,
validatePassword
validatePassword,
playDemoText
}
5 changes: 3 additions & 2 deletions ui/src/views/application/ApplicationSetting.vue
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,7 @@
</el-row>

<AIModeParamSettingDialog ref="AIModeParamSettingDialogRef" @refresh="refreshForm" />
<AIModeParamSettingDialog ref="TTSModeParamSettingDialogRef" @refresh="refreshTTSForm" />
<TTSModeParamSettingDialog ref="TTSModeParamSettingDialogRef" @refresh="refreshTTSForm" />
<ParamSettingDialog ref="ParamSettingDialogRef" @refresh="refreshParam" />
<AddDatasetDialog
ref="AddDatasetDialogRef"
Expand Down Expand Up @@ -573,6 +573,7 @@ import { relatedObject } from '@/utils/utils'
import { MsgSuccess, MsgWarning } from '@/utils/message'
import useStore from '@/stores'
import { t } from '@/locales'
import TTSModeParamSettingDialog from './component/TTSModeParamSettingDialog.vue'

const { model, application } = useStore()

Expand All @@ -587,7 +588,7 @@ const defaultPrompt = t('views.application.prompt.defaultPrompt', {
})

const AIModeParamSettingDialogRef = ref<InstanceType<typeof AIModeParamSettingDialog>>()
const TTSModeParamSettingDialogRef = ref<InstanceType<typeof AIModeParamSettingDialog>>()
const TTSModeParamSettingDialogRef = ref<InstanceType<typeof TTSModeParamSettingDialog>>()
const ParamSettingDialogRef = ref<InstanceType<typeof ParamSettingDialog>>()
const createModelRef = ref<InstanceType<typeof CreateModelDialog>>()
const selectProviderRef = ref<InstanceType<typeof SelectProviderDialog>>()
Expand Down
156 changes: 156 additions & 0 deletions ui/src/views/application/component/TTSModeParamSettingDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
<template>
<el-dialog
align-center
:title="$t('views.application.applicationForm.dialogues.paramSettings')"
class="aiMode-param-dialog"
v-model="dialogVisible"
style="width: 550px"
append-to-body
:close-on-click-modal="false"
:close-on-press-escape="false"
>
<DynamicsForm
v-model="form_data"
:model="form_data"
label-position="top"
require-asterisk-position="right"
:render_data="model_form_field"
ref="dynamicsFormRef"
>
</DynamicsForm>

<template #footer>
<div class="flex-between">
<span class="p-16">
<el-button @click="testPlay" :loading="playLoading">
<AppIcon iconName="app-video-play"></AppIcon>
试听
</el-button>
</span>
<span class="dialog-footer p-16">
<el-button @click.prevent="dialogVisible = false">
{{ $t('views.application.applicationForm.buttons.cancel') }}
</el-button>
<el-button type="primary" @click="submit" :loading="loading">
{{ $t('views.application.applicationForm.buttons.confirm') }}
</el-button>
</span>
</div>
</template>
</el-dialog>
<!-- 先渲染,不然不能播放 -->
<audio ref="audioPlayer" controls hidden="hidden"></audio>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import type { FormField } from '@/components/dynamics-form/type'
import modelAPi from '@/api/model'
import applicationApi from '@/api/application'
import DynamicsForm from '@/components/dynamics-form/index.vue'
import { keys } from 'lodash'
import { app } from '@/main'

const {
params: { id }
} = app.config.globalProperties.$route as any

const model_form_field = ref<Array<FormField>>([])
const emit = defineEmits(['refresh'])
const dynamicsFormRef = ref<InstanceType<typeof DynamicsForm>>()
const form_data = ref<any>({})
const dialogVisible = ref(false)
const loading = ref(false)
const playLoading = ref(false)
const getApi = (model_id: string, application_id?: string) => {
return application_id
? applicationApi.getModelParamsForm(application_id, model_id, loading)
: modelAPi.getModelParamsForm(model_id, loading)
}
const open = (model_id: string, application_id?: string, model_setting_data?: any) => {
form_data.value = {}
const api = getApi(model_id, application_id)
api.then((ok) => {
model_form_field.value = ok.data
model_setting_data =
model_setting_data && keys(model_setting_data).length > 0
? model_setting_data
: ok.data
.map((item: any) => ({ [item.field]: item.default_value }))
.reduce((x, y) => ({ ...x, ...y }), {})
// 渲染动态表单
dynamicsFormRef.value?.render(model_form_field.value, model_setting_data)
})
dialogVisible.value = true
}

const reset_default = (model_id: string, application_id?: string) => {
const api = getApi(model_id, application_id)
api.then((ok) => {
model_form_field.value = ok.data
const model_setting_data = ok.data
.map((item) => ({ [item.field]: item.default_value }))
.reduce((x, y) => ({ ...x, ...y }), {})

emit('refresh', model_setting_data)
})
}

const submit = async () => {
emit('refresh', form_data.value)
dialogVisible.value = false
}


const audioPlayer = ref<HTMLAudioElement | null>(null)
const testPlay = () => {
applicationApi
.playDemoText(id as string, form_data.value, playLoading)
.then((res: any) => {
// 创建 Blob 对象
const blob = new Blob([res], { type: 'audio/mp3' })

// 创建对象 URL
const url = URL.createObjectURL(blob)

// 检查 audioPlayer 是否已经引用了 DOM 元素
if (audioPlayer.value instanceof HTMLAudioElement) {
audioPlayer.value.src = url
audioPlayer.value.play() // 自动播放音频
} else {
console.error('audioPlayer.value is not an instance of HTMLAudioElement')
}
})
.catch((err) => {
console.log('err: ', err)
})

}


defineExpose({ open, reset_default })
</script>

<style lang="scss" scoped>
.aiMode-param-dialog {
padding: 8px 8px 24px 8px;

.el-dialog__header {
padding: 16px 16px 0 16px;
}

.el-dialog__body {
padding: 16px !important;
}

.dialog-max-height {
height: 550px;
}

.custom-slider {
.el-input-number.is-without-controls .el-input__wrapper {
padding: 0 !important;
}
}
}
</style>
4 changes: 2 additions & 2 deletions ui/src/workflow/nodes/base-node/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@

<FieldFormDialog ref="FieldFormDialogRef" @refresh="refreshFieldList" />
</NodeContainer>
<AIModeParamSettingDialog ref="TTSModeParamSettingDialogRef" @refresh="refreshTTSForm" />
<TTSModeParamSettingDialog ref="TTSModeParamSettingDialogRef" @refresh="refreshTTSForm" />
</template>
<script setup lang="ts">
import { app } from '@/main'
Expand All @@ -273,7 +273,7 @@ import type { Provider } from '@/api/type/model'
import FieldFormDialog from './component/FieldFormDialog.vue'
import { MsgError, MsgSuccess, MsgWarning } from '@/utils/message'
import { t } from '@/locales'
import AIModeParamSettingDialog from '@/views/application/component/AIModeParamSettingDialog.vue'
import TTSModeParamSettingDialog from '@/views/application/component/TTSModeParamSettingDialog.vue'
const { model } = useStore()

const {
Expand Down
Loading