From f9f475a5a031b55bfe0d2962800bc1bc6d45f3ef Mon Sep 17 00:00:00 2001 From: zhangzhanwei Date: Thu, 14 Aug 2025 11:19:25 +0800 Subject: [PATCH] feat: Integrate with Tencent SentienceRecognition model --- apps/locales/en_US/LC_MESSAGES/django.po | 78 +++++++++++++++ apps/locales/zh_CN/LC_MESSAGES/django.po | 80 +++++++++++++++- apps/locales/zh_Hant/LC_MESSAGES/django.po | 80 +++++++++++++++- .../tencent_model_provider/credential/stt.py | 90 ++++++++++++++++++ .../model/iat_mp3_16k.mp3 | Bin 0 -> 17876 bytes .../impl/tencent_model_provider/model/stt.py | 80 ++++++++++++++++ .../tencent_model_provider.py | 8 ++ apps/models_provider/tools.py | 5 +- pyproject.toml | 1 + 9 files changed, 418 insertions(+), 4 deletions(-) create mode 100644 apps/models_provider/impl/tencent_model_provider/credential/stt.py create mode 100644 apps/models_provider/impl/tencent_model_provider/model/iat_mp3_16k.mp3 create mode 100644 apps/models_provider/impl/tencent_model_provider/model/stt.py diff --git a/apps/locales/en_US/LC_MESSAGES/django.po b/apps/locales/en_US/LC_MESSAGES/django.po index ee62e792a..4f6fa10e3 100644 --- a/apps/locales/en_US/LC_MESSAGES/django.po +++ b/apps/locales/en_US/LC_MESSAGES/django.po @@ -8570,4 +8570,82 @@ msgid "Edit user authorization status of resource" msgstr "" msgid "Get user authorization status of resource by page" +msgstr "" + +msgid "Obtain resource authorization list by page" +msgstr "" + +msgid "Engine model type" +msgstr "" + +msgid "If not passed, the default value is 16k_zh (Chinese universal)" +msgstr "" + +msgid "Chinese telephone universal" +msgstr "" + +msgid "English telephone universal" +msgstr "" + +msgid "Commonly used in Chinese" +msgstr "" + +msgid "Chinese, English, and Guangdong" +msgstr "" + +msgid "Chinese medical" +msgstr "" + +msgid "English" +msgstr "" + +msgid "Cantonese" +msgstr "" + +msgid "Japanese" +msgstr "" + +msgid "Korean" +msgstr "" + +msgid "Vietnamese" +msgstr "" + +msgid "Malay language" +msgstr "" + +msgid "Indonesian language" +msgstr "" + +msgid "Filipino language" +msgstr "" + +msgid "Thai" +msgstr "" + +msgid "Portuguese" +msgstr "" + +msgid "Turkish" +msgstr "" + +msgid "Arabic" +msgstr "" + +msgid "Spanish" +msgstr "" + +msgid "Hindi" +msgstr "" + +msgid "French" +msgstr "" + +msgid "German" +msgstr "" + +msgid "Multiple dialects, supporting 23 dialects" +msgstr "" + +msgid "This interface is used to recognize short audio files within 60 seconds. Supports Mandarin Chinese, English, Cantonese, Japanese, Vietnamese, Malay, Indonesian, Filipino, Thai, Portuguese, Turkish, Arabic, Hindi, French, German, and 23 Chinese dialects." msgstr "" \ No newline at end of file diff --git a/apps/locales/zh_CN/LC_MESSAGES/django.po b/apps/locales/zh_CN/LC_MESSAGES/django.po index 36bc809ab..62e97f258 100644 --- a/apps/locales/zh_CN/LC_MESSAGES/django.po +++ b/apps/locales/zh_CN/LC_MESSAGES/django.po @@ -8696,4 +8696,82 @@ msgid "Edit user authorization status of resource" msgstr "修改资源对用户的授权状态" msgid "Get user authorization status of resource by page" -msgstr "分页获取资源对用户的授权状态" \ No newline at end of file +msgstr "分页获取资源对用户的授权状态" + +msgid "Obtain resource authorization list by page" +msgstr "分页获取资源授权列表" + +msgid "Engine model type" +msgstr "引擎模型类型" + +msgid "If not passed, the default value is 16k_zh (Chinese universal)" +msgstr "如果未传递,默认值为 16k_zh(中文通用)" + +msgid "Chinese telephone universal" +msgstr "中文电话通用" + +msgid "English telephone universal" +msgstr "英文电话通用" + +msgid "Commonly used in Chinese" +msgstr "中文常用" + +msgid "Chinese, English, and Guangdong" +msgstr "中文、英文和广东话" + +msgid "Chinese medical" +msgstr "中文医疗" + +msgid "English" +msgstr "英文" + +msgid "Cantonese" +msgstr "粤语" + +msgid "Japanese" +msgstr "日语" + +msgid "Korean" +msgstr "韩语" + +msgid "Vietnamese" +msgstr "越南语" + +msgid "Malay language" +msgstr "马来语" + +msgid "Indonesian language" +msgstr "印尼语" + +msgid "Filipino language" +msgstr "菲律宾语" + +msgid "Thai" +msgstr "泰语" + +msgid "Portuguese" +msgstr "葡萄牙语" + +msgid "Turkish" +msgstr "土耳其语" + +msgid "Arabic" +msgstr "阿拉伯语" + +msgid "Spanish" +msgstr "西班牙语" + +msgid "Hindi" +msgstr "印地语" + +msgid "French" +msgstr "法语" + +msgid "German" +msgstr "德语" + +msgid "Multiple dialects, supporting 23 dialects" +msgstr "多种方言,支持 23 种方言" + +msgid "This interface is used to recognize short audio files within 60 seconds. Supports Mandarin Chinese, English, Cantonese, Japanese, Vietnamese, Malay, Indonesian, Filipino, Thai, Portuguese, Turkish, Arabic, Hindi, French, German, and 23 Chinese dialects." +msgstr "本接口用于识别 60 秒之内的短音频文件。支持中文普通话、英语、粤语、日语、越南语、马来语、印度尼西亚语、菲律宾语、泰语、葡萄牙语、土耳其语、阿拉伯语、印地语、法语、德语及 23 种汉语方言。" \ No newline at end of file diff --git a/apps/locales/zh_Hant/LC_MESSAGES/django.po b/apps/locales/zh_Hant/LC_MESSAGES/django.po index efcaf4749..794a0f859 100644 --- a/apps/locales/zh_Hant/LC_MESSAGES/django.po +++ b/apps/locales/zh_Hant/LC_MESSAGES/django.po @@ -8696,4 +8696,82 @@ msgid "Edit user authorization status of resource" msgstr "修改資源對用戶的授權狀態" msgid "Get user authorization status of resource by page" -msgstr "分頁獲取資源對用戶的授權狀態" \ No newline at end of file +msgstr "分頁獲取資源對用戶的授權狀態" + +msgid "Obtain resource authorization list by page" +msgstr "分頁獲取資源授權清單" + +msgid "Engine model type" +msgstr "引擎模型類型" + +msgid "If not passed, the default value is 16k_zh (Chinese universal)" +msgstr "如果未傳遞,默認值為 16k_zh(中文通用)" + +msgid "Chinese telephone universal" +msgstr "中文電話通用" + +msgid "English telephone universal" +msgstr "英文電話通用" + +msgid "Commonly used in Chinese" +msgstr "中文常用" + +msgid "Chinese, English, and Guangdong" +msgstr "中文、英文和廣東話" + +msgid "Chinese medical" +msgstr "中文醫療" + +msgid "English" +msgstr "英文" + +msgid "Cantonese" +msgstr "粵語" + +msgid "Japanese" +msgstr "日語" + +msgid "Korean" +msgstr "韓語" + +msgid "Vietnamese" +msgstr "越南語" + +msgid "Malay language" +msgstr "馬來語" + +msgid "Indonesian language" +msgstr "印尼語" + +msgid "Filipino language" +msgstr "菲律賓語" + +msgid "Thai" +msgstr "泰語" + +msgid "Portuguese" +msgstr "葡萄牙語" + +msgid "Turkish" +msgstr "土耳其語" + +msgid "Arabic" +msgstr "阿拉伯語" + +msgid "Spanish" +msgstr "西班牙語" + +msgid "Hindi" +msgstr "印地語" + +msgid "French" +msgstr "法語" + +msgid "German" +msgstr "德語" + +msgid "Multiple dialects, supporting 23 dialects" +msgstr "多種方言,支持 23 種方言" + +msgid "This interface is used to recognize short audio files within 60 seconds. Supports Mandarin Chinese, English, Cantonese, Japanese, Vietnamese, Malay, Indonesian, Filipino, Thai, Portuguese, Turkish, Arabic, Hindi, French, German, and 23 Chinese dialects." +msgstr "本介面用於識別 60 秒之內的短音頻文件。支援中文普通話、英語、粵語、日語、越南語、馬來語、印度尼西亞語、菲律賓語、泰語、葡萄牙語、土耳其語、阿拉伯語、印地語、法語、德語及 23 種漢語方言。" \ No newline at end of file diff --git a/apps/models_provider/impl/tencent_model_provider/credential/stt.py b/apps/models_provider/impl/tencent_model_provider/credential/stt.py new file mode 100644 index 000000000..3eea500f2 --- /dev/null +++ b/apps/models_provider/impl/tencent_model_provider/credential/stt.py @@ -0,0 +1,90 @@ +import traceback + +from common import forms +from common.exception.app_exception import AppApiException +from common.forms import BaseForm, TooltipLabel +from django.utils.translation import gettext_lazy as _, gettext + +from models_provider.base_model_provider import BaseModelCredential, ValidCode + + +class TencentSSTModelParams(BaseForm): + EngSerViceType = forms.SingleSelect( + TooltipLabel(_('Engine model type'), _('If not passed, the default value is 16k_zh (Chinese universal)')), + required=True, + default_value='16k_zh', + option_list=[ + {"value": "8k_zh", "label": _("Chinese telephone universal")}, + {"value": "8k_en", "label": _("English telephone universal")}, + {"value": "16k_zh", "label": _("Commonly used in Chinese")}, + {"value": "16k_zh-PY", "label": _("Chinese, English, and Guangdong")}, + {"value": "16k_zh_medical", "label": _("Chinese medical")}, + {"value": "16k_en", "label": _("English")}, + {"value": "16k_yue", "label": _("Cantonese")}, + {"value": "16k_ja", "label": _("Japanese")}, + {"value": "16k_ko", "label": _("Korean")}, + {"value": "16k_vi", "label": _("Vietnamese")}, + {"value": "16k_ms", "label": _("Malay language")}, + {"value": "16k_id", "label": _("Indonesian language")}, + {"value": "16k_fil", "label": _("Filipino language")}, + {"value": "16k_th", "label": _("Thai")}, + {"value": "16k_pt", "label": _("Portuguese")}, + {"value": "16k_tr", "label": _("Turkish")}, + {"value": "16k_ar", "label": _("Arabic")}, + {"value": "16k_es", "label": _("Spanish")}, + {"value": "16k_hi", "label": _("Hindi")}, + {"value": "16k_fr", "label": _("French")}, + {"value": "16k_de", "label": _("German")}, + {"value": "16k_zh_dialect", "label": _("Multiple dialects, supporting 23 dialects")} + ], + value_field='value', + text_field='label' + ) + +class TencentSTTModelCredential(BaseForm, BaseModelCredential): + REQUIRED_FIELDS = ["SecretId", "SecretKey"] + + @classmethod + def _validate_model_type(cls, model_type, provider, raise_exception=False): + if not any(mt['value'] == model_type for mt in provider.get_model_type_list()): + if raise_exception: + raise AppApiException(ValidCode.valid_error.value, + gettext('{model_type} Model type is not supported').format(model_type=model_type)) + return False + return True + + @classmethod + def _validate_credential_fields(cls, model_credential, raise_exception=False): + missing_keys = [key for key in cls.REQUIRED_FIELDS if key not in model_credential] + if missing_keys: + if raise_exception: + raise AppApiException(ValidCode.valid_error.value, + gettext('{keys} is required').format(keys=", ".join(missing_keys))) + return False + return True + + def is_valid(self, model_type, model_name, model_credential, model_params, provider, raise_exception=False): + if not (self._validate_model_type(model_type, provider, raise_exception) and + self._validate_credential_fields(model_credential, raise_exception)): + return False + try: + model = provider.get_model(model_type, model_name, model_credential, **model_params) + model.check_auth() + except Exception as e: + traceback.print_exc() + if raise_exception: + raise AppApiException(ValidCode.valid_error.value, + gettext( + 'Verification failed, please check whether the parameters are correct: {error}').format( + error=str(e))) + return False + return True + + def encryption_dict(self, model): + return {**model, 'SecretKey': super().encryption(model.get('SecretKey', ''))} + + SecretId = forms.PasswordInputField('SecretId', required=True) + SecretKey = forms.PasswordInputField('SecretKey', required=True) + + def get_model_params_setting_form(self, model_name): + return TencentSSTModelParams() diff --git a/apps/models_provider/impl/tencent_model_provider/model/iat_mp3_16k.mp3 b/apps/models_provider/impl/tencent_model_provider/model/iat_mp3_16k.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..75e744c8ff5188208a3797561efb2e096d4fa015 GIT binary patch literal 17876 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