Skip to content

Commit 532eee5

Browse files
feat: enhance TTS model parameters and API version handling (#4480)
Co-authored-by: xiaoc <648844981@qq.com>
1 parent 4e58d07 commit 532eee5

File tree

5 files changed

+494
-3
lines changed

5 files changed

+494
-3
lines changed
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# coding=utf-8
2+
"""
3+
讯飞 TTS 工厂类 Credential,根据 api_version 路由到具体 Credential
4+
"""
5+
from typing import Dict
6+
7+
from django.utils.translation import gettext_lazy as _, gettext
8+
9+
from common import forms
10+
from common.exception.app_exception import AppApiException
11+
from common.forms import BaseForm, TooltipLabel
12+
from models_provider.base_model_provider import BaseModelCredential, ValidCode
13+
from common.utils.logger import maxkb_logger
14+
15+
16+
class XunFeiDefaultTTSModelCredential(BaseForm, BaseModelCredential):
17+
"""讯飞 TTS 工厂类 Credential,根据 api_version 参数路由到具体实现"""
18+
19+
api_version = forms.SingleSelect(
20+
_("API Version"), required=True,
21+
text_field='label',
22+
value_field='value',
23+
default_value='online',
24+
option_list=[
25+
{'label': _('Online TTS'), 'value': 'online'},
26+
{'label': _('Super Humanoid TTS'), 'value': 'super_humanoid'}
27+
])
28+
29+
spark_api_url = forms.TextInputField('API URL', required=True,
30+
default_value='wss://tts-api.xfyun.cn/v2/tts',
31+
relation_show_field_dict={"api_version": ["online"]})
32+
spark_api_url_super = forms.TextInputField('API URL', required=True,
33+
default_value='wss://cbm01.cn-huabei-1.xf-yun.com/v1/private/mcd9m97e6',
34+
relation_show_field_dict={"api_version": ["super_humanoid"]})
35+
36+
# vcn 选择放在 credential 中,根据 api_version 联动显示
37+
vcn_online = forms.SingleSelect(
38+
TooltipLabel(_('Speaker'), _('Speaker selection for standard TTS service')),
39+
required=True, default_value='xiaoyan',
40+
text_field='value',
41+
value_field='value',
42+
option_list=[
43+
{'text': _('iFlytek Xiaoyan'), 'value': 'xiaoyan'},
44+
{'text': _('iFlytek Xujiu'), 'value': 'aisjiuxu'},
45+
{'text': _('iFlytek Xiaoping'), 'value': 'aisxping'},
46+
{'text': _('iFlytek Xiaojing'), 'value': 'aisjinger'},
47+
{'text': _('iFlytek Xuxiaobao'), 'value': 'aisbabyxu'},
48+
],
49+
relation_show_field_dict={"api_version": ["online"]})
50+
51+
vcn_super = forms.SingleSelect(
52+
TooltipLabel(_('Speaker'), _('Speaker selection for super-humanoid TTS service')),
53+
required=True, default_value='x5_lingxiaoxuan_flow',
54+
text_field='value',
55+
value_field='value',
56+
option_list=[
57+
{'text': _('Super-humanoid: Lingxiaoxuan Flow'), 'value': 'x5_lingxiaoxuan_flow'},
58+
{'text': _('Super-humanoid: Lingyuyan Flow'), 'value': 'x5_lingyuyan_flow'},
59+
{'text': _('Super-humanoid: Lingfeiyi Flow'), 'value': 'x5_lingfeiyi_flow'},
60+
{'text': _('Super-humanoid: Lingxiaoyue Flow'), 'value': 'x5_lingxiaoyue_flow'},
61+
{'text': _('Super-humanoid: Sun Dasheng Flow'), 'value': 'x5_sundasheng_flow'},
62+
{'text': _('Super-humanoid: Lingyuzhao Flow'), 'value': 'x5_lingyuzhao_flow'},
63+
{'text': _('Super-humanoid: Lingxiaotang Flow'), 'value': 'x5_lingxiaotang_flow'},
64+
{'text': _('Super-humanoid: Lingxiaorong Flow'), 'value': 'x5_lingxiaorong_flow'},
65+
{'text': _('Super-humanoid: Xinyun Flow'), 'value': 'x5_xinyun_flow'},
66+
{'text': _('Super-humanoid: Grant (EN)'), 'value': 'x5_EnUs_Grant_flow'},
67+
{'text': _('Super-humanoid: Lila (EN)'), 'value': 'x5_EnUs_Lila_flow'},
68+
{'text': _('Super-humanoid: Lingwanwan Pro'), 'value': 'x6_lingwanwan_pro'},
69+
{'text': _('Super-humanoid: Yiyi Pro'), 'value': 'x6_yiyi_pro'},
70+
{'text': _('Super-humanoid: Huifangnv Pro'), 'value': 'x6_huifangnv_pro'},
71+
{'text': _('Super-humanoid: Lingxiaoying Pro'), 'value': 'x6_lingxiaoying_pro'},
72+
{'text': _('Super-humanoid: Lingfeibo Pro'), 'value': 'x6_lingfeibo_pro'},
73+
{'text': _('Super-humanoid: Lingyuyan Pro'), 'value': 'x6_lingyuyan_pro'},
74+
],
75+
relation_show_field_dict={"api_version": ["super_humanoid"]})
76+
77+
spark_app_id = forms.TextInputField('APP ID', required=True)
78+
spark_api_key = forms.PasswordInputField("API Key", required=True)
79+
spark_api_secret = forms.PasswordInputField('API Secret', required=True)
80+
81+
def is_valid(self, model_type: str, model_name, model_credential: Dict[str, object], model_params, provider,
82+
raise_exception=False):
83+
model_type_list = provider.get_model_type_list()
84+
if not any(list(filter(lambda mt: mt.get('value') == model_type, model_type_list))):
85+
raise AppApiException(ValidCode.valid_error.value,
86+
gettext('{model_type} Model type is not supported').format(model_type=model_type))
87+
88+
api_version = model_credential.get('api_version', 'online')
89+
if api_version == 'super_humanoid':
90+
required_keys = ['spark_api_url_super', 'spark_app_id', 'spark_api_key', 'spark_api_secret']
91+
else:
92+
required_keys = ['spark_api_url', 'spark_app_id', 'spark_api_key', 'spark_api_secret']
93+
94+
for key in required_keys:
95+
if key not in model_credential:
96+
if raise_exception:
97+
raise AppApiException(ValidCode.valid_error.value, gettext('{key} is required').format(key=key))
98+
else:
99+
return False
100+
try:
101+
model = provider.get_model(model_type, model_name, model_credential, **model_params)
102+
model.check_auth()
103+
except Exception as e:
104+
maxkb_logger.error(f'Exception: {e}', exc_info=True)
105+
if isinstance(e, AppApiException):
106+
raise e
107+
if raise_exception:
108+
raise AppApiException(ValidCode.valid_error.value,
109+
gettext(
110+
'Verification failed, please check whether the parameters are correct: {error}').format(
111+
error=str(e)))
112+
else:
113+
return False
114+
return True
115+
116+
def encryption_dict(self, model: Dict[str, object]):
117+
return {**model, 'spark_api_secret': super().encryption(model.get('spark_api_secret', ''))}
118+
119+
def get_model_params_setting_form(self, model_name):
120+
# params 只包含通用参数,vcn 已在 credential 中
121+
return XunFeiDefaultTTSModelParams()
122+
123+
124+
class XunFeiDefaultTTSModelParams(BaseForm):
125+
"""工厂类的参数表单,只包含通用参数"""
126+
127+
speed = forms.SliderField(
128+
TooltipLabel(_('speaking speed'), _('Speech speed, optional value: [0-100], default is 50')),
129+
required=True, default_value=50,
130+
_min=1,
131+
_max=100,
132+
_step=5,
133+
precision=1)
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# coding=utf-8
2+
"""
3+
讯飞超拟人语音合成 (Super Humanoid TTS) Credential
4+
"""
5+
from typing import Dict
6+
7+
from django.utils.translation import gettext_lazy as _, gettext
8+
9+
from common import forms
10+
from common.exception.app_exception import AppApiException
11+
from common.forms import BaseForm, TooltipLabel
12+
from models_provider.base_model_provider import BaseModelCredential, ValidCode
13+
14+
15+
class XunFeiSuperHumanoidTTSModelParams(BaseForm):
16+
"""超拟人语音合成参数"""
17+
vcn = forms.SingleSelect(
18+
TooltipLabel(_('Speaker'), _('Speaker selection for super-humanoid TTS service')),
19+
required=True, default_value='x5_lingxiaoxuan_flow',
20+
text_field='value',
21+
value_field='value',
22+
option_list=[
23+
{'text': _('Super-humanoid: Lingxiaoxuan Flow'), 'value': 'x5_lingxiaoxuan_flow'},
24+
{'text': _('Super-humanoid: Lingyuyan Flow'), 'value': 'x5_lingyuyan_flow'},
25+
{'text': _('Super-humanoid: Lingfeiyi Flow'), 'value': 'x5_lingfeiyi_flow'},
26+
{'text': _('Super-humanoid: Lingxiaoyue Flow'), 'value': 'x5_lingxiaoyue_flow'},
27+
{'text': _('Super-humanoid: Sun Dasheng Flow'), 'value': 'x5_sundasheng_flow'},
28+
{'text': _('Super-humanoid: Lingyuzhao Flow'), 'value': 'x5_lingyuzhao_flow'},
29+
{'text': _('Super-humanoid: Lingxiaotang Flow'), 'value': 'x5_lingxiaotang_flow'},
30+
{'text': _('Super-humanoid: Lingxiaorong Flow'), 'value': 'x5_lingxiaorong_flow'},
31+
{'text': _('Super-humanoid: Xinyun Flow'), 'value': 'x5_xinyun_flow'},
32+
{'text': _('Super-humanoid: Grant (EN)'), 'value': 'x5_EnUs_Grant_flow'},
33+
{'text': _('Super-humanoid: Lila (EN)'), 'value': 'x5_EnUs_Lila_flow'},
34+
{'text': _('Super-humanoid: Lingwanwan Pro'), 'value': 'x6_lingwanwan_pro'},
35+
{'text': _('Super-humanoid: Yiyi Pro'), 'value': 'x6_yiyi_pro'},
36+
{'text': _('Super-humanoid: Huifangnv Pro'), 'value': 'x6_huifangnv_pro'},
37+
{'text': _('Super-humanoid: Lingxiaoying Pro'), 'value': 'x6_lingxiaoying_pro'},
38+
{'text': _('Super-humanoid: Lingfeibo Pro'), 'value': 'x6_lingfeibo_pro'},
39+
{'text': _('Super-humanoid: Lingyuyan Pro'), 'value': 'x6_lingyuyan_pro'},
40+
])
41+
42+
speed = forms.SliderField(
43+
TooltipLabel(_('speaking speed'), _('Speech speed, optional value: [0-100], default is 50')),
44+
required=True, default_value=50,
45+
_min=1,
46+
_max=100,
47+
_step=5,
48+
precision=1)
49+
50+
51+
class XunFeiSuperHumanoidTTSModelCredential(BaseForm, BaseModelCredential):
52+
"""讯飞超拟人语音合成 Credential"""
53+
spark_api_url = forms.TextInputField('API URL', required=True,
54+
default_value='wss://cbm01.cn-huabei-1.xf-yun.com/v1/private/mcd9m97e6')
55+
spark_app_id = forms.TextInputField('APP ID', required=True)
56+
spark_api_key = forms.PasswordInputField("API Key", required=True)
57+
spark_api_secret = forms.PasswordInputField('API Secret', required=True)
58+
59+
def is_valid(self, model_type: str, model_name, model_credential: Dict[str, object], model_params, provider,
60+
raise_exception=False):
61+
model_type_list = provider.get_model_type_list()
62+
if not any(list(filter(lambda mt: mt.get('value') == model_type, model_type_list))):
63+
raise AppApiException(ValidCode.valid_error.value,
64+
gettext('{model_type} Model type is not supported').format(model_type=model_type))
65+
66+
required_keys = ['spark_api_url', 'spark_app_id', 'spark_api_key', 'spark_api_secret']
67+
68+
for key in required_keys:
69+
if key not in model_credential:
70+
if raise_exception:
71+
raise AppApiException(ValidCode.valid_error.value, gettext('{key} is required').format(key=key))
72+
else:
73+
return False
74+
try:
75+
model = provider.get_model(model_type, model_name, model_credential, **model_params)
76+
model.check_auth()
77+
except Exception as e:
78+
if isinstance(e, AppApiException):
79+
raise e
80+
if raise_exception:
81+
raise AppApiException(ValidCode.valid_error.value,
82+
gettext(
83+
'Verification failed, please check whether the parameters are correct: {error}').format(
84+
error=str(e)))
85+
else:
86+
return False
87+
return True
88+
89+
def encryption_dict(self, model: Dict[str, object]):
90+
return {**model, 'spark_api_secret': super().encryption(model.get('spark_api_secret', ''))}
91+
92+
def get_model_params_setting_form(self, model_name):
93+
return XunFeiSuperHumanoidTTSModelParams()
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# coding=utf-8
2+
"""
3+
@project: MaxKB
4+
@Author:
5+
@file: default_tts.py
6+
@date:2025/12/9
7+
@desc: 讯飞 TTS 工厂类,根据 api_version 路由到具体实现
8+
"""
9+
from typing import Dict
10+
11+
from models_provider.base_model_provider import MaxKBBaseModel
12+
from models_provider.impl.base_tts import BaseTextToSpeech
13+
14+
15+
class XFSparkDefaultTextToSpeech(MaxKBBaseModel, BaseTextToSpeech):
16+
"""讯飞 TTS 工厂类,根据 api_version 参数路由到具体实现"""
17+
18+
def check_auth(self):
19+
pass
20+
21+
def text_to_speech(self, text):
22+
pass
23+
24+
@staticmethod
25+
def is_cache_model():
26+
return False
27+
28+
@staticmethod
29+
def new_instance(model_type, model_name, model_credential: Dict[str, object], **model_kwargs):
30+
from models_provider.impl.xf_model_provider.model.tts import XFSparkTextToSpeech
31+
from models_provider.impl.xf_model_provider.model.super_humanoid_tts import XFSparkSuperHumanoidTextToSpeech
32+
33+
api_version = model_credential.get('api_version', 'online')
34+
35+
if api_version == 'super_humanoid':
36+
# 超拟人:从 credential 获取 vcn_super,构造统一的 credential 格式
37+
vcn = model_credential.get('vcn_super', 'x5_lingxiaoxuan_flow')
38+
unified_credential = {
39+
'spark_app_id': model_credential.get('spark_app_id'),
40+
'spark_api_key': model_credential.get('spark_api_key'),
41+
'spark_api_secret': model_credential.get('spark_api_secret'),
42+
'spark_api_url': model_credential.get('spark_api_url_super'),
43+
}
44+
return XFSparkSuperHumanoidTextToSpeech.new_instance(
45+
model_type, model_name, unified_credential, vcn=vcn, **model_kwargs
46+
)
47+
else:
48+
# 在线语音:从 credential 获取 vcn_online
49+
vcn = model_credential.get('vcn_online', 'xiaoyan')
50+
unified_credential = {
51+
'spark_app_id': model_credential.get('spark_app_id'),
52+
'spark_api_key': model_credential.get('spark_api_key'),
53+
'spark_api_secret': model_credential.get('spark_api_secret'),
54+
'spark_api_url': model_credential.get('spark_api_url'),
55+
}
56+
return XFSparkTextToSpeech.new_instance(
57+
model_type, model_name, unified_credential, vcn=vcn, **model_kwargs
58+
)

0 commit comments

Comments
 (0)