mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-22 07:03:53 +08:00
Merge pull request #1320 from xinnan-tech/manager-icp-num
可自定义智控台底部的系统名称、ICP备案号、公安备案号
This commit is contained in:
@@ -40,5 +40,5 @@
|
||||
|
||||
2、开启允许非管理员用户可注册,将参数`server.allow_user_register`设置成`true`
|
||||
|
||||
3、开启手机注册功能,将参数`system.enable_mobile_register`设置成`true`
|
||||
3、开启手机注册功能,将参数`server.enable_mobile_register`设置成`true`
|
||||

|
||||
@@ -118,29 +118,17 @@ public interface Constant {
|
||||
|
||||
enum SysBaseParam {
|
||||
/**
|
||||
* 系统全称
|
||||
* ICP备案号
|
||||
*/
|
||||
SYS_NAME("SYS_NAME"),
|
||||
BEIAN_ICP_NUM("server.beian_icp_num"),
|
||||
/**
|
||||
* 系统简称
|
||||
* GA备案号
|
||||
*/
|
||||
SYS_SHORT_NAME("SYS_SHORT_NAME"),
|
||||
BEIAN_GA_NUM("server.beian_ga_num"),
|
||||
/**
|
||||
* 系统描述
|
||||
* 系统名称
|
||||
*/
|
||||
SYS_DES("SYS_DES"),
|
||||
/**
|
||||
* 登录失败几次锁定
|
||||
*/
|
||||
LOGIN_LOCK_COUNT("LOGIN_LOCK_COUNT"),
|
||||
/**
|
||||
* 账号失败锁定分钟数
|
||||
*/
|
||||
LOGIN_LOCK_TIME("LOGIN_LOCK_TIME"),
|
||||
/**
|
||||
* TOKEN强验证
|
||||
*/
|
||||
SYS_TOKEN_SECURITY("SYS_TOKEN_SECURITY");
|
||||
SERVER_NAME("server.name");
|
||||
|
||||
private String value;
|
||||
|
||||
@@ -176,11 +164,11 @@ public interface Constant {
|
||||
/**
|
||||
* 单号码最大短信发送条数
|
||||
*/
|
||||
SYSTEM_SMS_MAX_SEND_COUNT("system.sms.max_send_count"),
|
||||
SERVER_SMS_MAX_SEND_COUNT("server.sms_max_send_count"),
|
||||
/**
|
||||
* 是否开启手机注册
|
||||
*/
|
||||
SYSTEM_ENABLE_MOBILE_REGISTER("system.enable_mobile_register");
|
||||
SERVER_ENABLE_MOBILE_REGISTER("server.enable_mobile_register");
|
||||
|
||||
private String value;
|
||||
|
||||
@@ -245,4 +233,24 @@ public interface Constant {
|
||||
* 无效固件URL
|
||||
*/
|
||||
String INVALID_FIRMWARE_URL = "http://xiaozhi.server.com:8002/xiaozhi/otaMag/download/NOT_ACTIVATED_FIRMWARE_THIS_IS_A_INVALID_URL";
|
||||
|
||||
/**
|
||||
* 字典类型
|
||||
*/
|
||||
enum DictType {
|
||||
/**
|
||||
* 手机区号
|
||||
*/
|
||||
MOBILE_AREA("MOBILE_AREA");
|
||||
|
||||
private String value;
|
||||
|
||||
DictType(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-5
@@ -1,6 +1,7 @@
|
||||
package xiaozhi.modules.security.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -70,7 +71,7 @@ public class LoginController {
|
||||
throw new RenException("图形验证码错误");
|
||||
}
|
||||
Boolean isMobileRegister = sysParamsService
|
||||
.getValueObject(Constant.SysMSMParam.SYSTEM_ENABLE_MOBILE_REGISTER.getValue(), Boolean.class);
|
||||
.getValueObject(Constant.SysMSMParam.SERVER_ENABLE_MOBILE_REGISTER.getValue(), Boolean.class);
|
||||
if (!isMobileRegister) {
|
||||
throw new RenException("没有开启手机注册,没法使用短信验证码功能");
|
||||
}
|
||||
@@ -108,7 +109,7 @@ public class LoginController {
|
||||
}
|
||||
// 是否开启手机注册
|
||||
Boolean isMobileRegister = sysParamsService
|
||||
.getValueObject(Constant.SysMSMParam.SYSTEM_ENABLE_MOBILE_REGISTER.getValue(), Boolean.class);
|
||||
.getValueObject(Constant.SysMSMParam.SERVER_ENABLE_MOBILE_REGISTER.getValue(), Boolean.class);
|
||||
boolean validate;
|
||||
if (isMobileRegister) {
|
||||
// 验证用户是否是手机号码
|
||||
@@ -165,7 +166,7 @@ public class LoginController {
|
||||
public Result<?> retrievePassword(@RequestBody RetrievePasswordDTO dto) {
|
||||
// 是否开启手机注册
|
||||
Boolean isMobileRegister = sysParamsService
|
||||
.getValueObject(Constant.SysMSMParam.SYSTEM_ENABLE_MOBILE_REGISTER.getValue(), Boolean.class);
|
||||
.getValueObject(Constant.SysMSMParam.SERVER_ENABLE_MOBILE_REGISTER.getValue(), Boolean.class);
|
||||
if (!isMobileRegister) {
|
||||
throw new RenException("没有开启手机注册,没法使用找回密码功能");
|
||||
}
|
||||
@@ -198,11 +199,15 @@ public class LoginController {
|
||||
public Result<Map<String, Object>> pubConfig() {
|
||||
Map<String, Object> config = new HashMap<>();
|
||||
config.put("enableMobileRegister", sysParamsService
|
||||
.getValueObject(Constant.SysMSMParam.SYSTEM_ENABLE_MOBILE_REGISTER.getValue(), Boolean.class));
|
||||
.getValueObject(Constant.SysMSMParam.SERVER_ENABLE_MOBILE_REGISTER.getValue(), Boolean.class));
|
||||
config.put("version", Constant.VERSION);
|
||||
config.put("year", "©" + Calendar.getInstance().get(Calendar.YEAR));
|
||||
config.put("allowUserRegister", sysUserService.getAllowUserRegister());
|
||||
List<SysDictDataItem> list = sysDictDataService.getDictDataByType("MOBILE_AREA");
|
||||
List<SysDictDataItem> list = sysDictDataService.getDictDataByType(Constant.DictType.MOBILE_AREA.getValue());
|
||||
config.put("mobileAreaList", list);
|
||||
config.put("beianIcpNum", sysParamsService.getValue(Constant.SysBaseParam.BEIAN_ICP_NUM.getValue(), true));
|
||||
config.put("beianGaNum", sysParamsService.getValue(Constant.SysBaseParam.BEIAN_GA_NUM.getValue(), true));
|
||||
config.put("name", sysParamsService.getValue(Constant.SysBaseParam.SERVER_NAME.getValue(), true));
|
||||
|
||||
return new Result<Map<String, Object>>().ok(config);
|
||||
}
|
||||
|
||||
+1
-1
@@ -101,7 +101,7 @@ public class CaptchaServiceImpl implements CaptchaService {
|
||||
|
||||
// 获取最大发送次数限制
|
||||
Integer maxSendCount = sysParamsService.getValueObject(
|
||||
Constant.SysMSMParam.SYSTEM_SMS_MAX_SEND_COUNT.getValue(),
|
||||
Constant.SysMSMParam.SERVER_SMS_MAX_SEND_COUNT.getValue(),
|
||||
Integer.class);
|
||||
if (maxSendCount == null) {
|
||||
maxSendCount = 5; // 默认值
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ public class SysParamsController {
|
||||
@Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY, required = true, ref = "int"),
|
||||
@Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref = "String"),
|
||||
@Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref = "String"),
|
||||
@Parameter(name = "paramCode", description = "参数编码", in = ParameterIn.QUERY, ref = "String")
|
||||
@Parameter(name = "paramCode", description = "参数编码或参数备注", in = ParameterIn.QUERY, ref = "String")
|
||||
})
|
||||
@RequiresPermissions("sys:role:superAdmin")
|
||||
public Result<PageData<SysParamsDTO>> page(@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
|
||||
|
||||
+3
-1
@@ -52,7 +52,9 @@ public class SysParamsServiceImpl extends BaseServiceImpl<SysParamsDao, SysParam
|
||||
|
||||
QueryWrapper<SysParamsEntity> wrapper = new QueryWrapper<>();
|
||||
wrapper.eq("param_type", 1);
|
||||
wrapper.like(StringUtils.isNotBlank(paramCode), "param_code", paramCode);
|
||||
wrapper.nested(StringUtils.isNotBlank(paramCode),i->i.like("param_code", paramCode)
|
||||
.or()
|
||||
.like("remark", paramCode));
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
+9
-3
@@ -1,15 +1,21 @@
|
||||
-- 添加手机短信注册功能的需要的参数
|
||||
delete from sys_params where id in (601, 602, 610, 611, 612, 613);
|
||||
delete from sys_params where id in (108, 109, 110, 111, 112, 113, 114, 115);
|
||||
delete from sys_params where id in (610, 611, 612, 613);
|
||||
INSERT INTO sys_params
|
||||
(id, param_code, param_value, value_type, param_type, remark, creator, create_date, updater, update_date)
|
||||
VALUES
|
||||
(601, 'system.enable_mobile_register', 'false', 'boolean', 1, '是否开启手机注册', NULL, NULL, NULL, NULL),
|
||||
(602, 'system.sms.max_send_count', '10', 'number', 1, '单号码单日最大短信发送条数', NULL, NULL, NULL, NULL),
|
||||
(108, 'server.name', 'xiaozhi-esp32-server', 'string', 1, '系统名称', NULL, NULL, NULL, NULL),
|
||||
(109, 'server.beian_icp_num', 'null', 'string', 1, 'icp备案号,填写null则不设置', NULL, NULL, NULL, NULL),
|
||||
(110, 'server.beian_ga_num', 'null', 'string', 1, '公安备案号,填写null则不设置', NULL, NULL, NULL, NULL),
|
||||
(111, 'server.enable_mobile_register', 'false', 'boolean', 1, '是否开启手机注册', NULL, NULL, NULL, NULL),
|
||||
(112, 'server.sms_max_send_count', '10', 'number', 1, '单号码单日最大短信发送条数', NULL, NULL, NULL, NULL),
|
||||
(610, 'aliyun.sms.access_key_id', '', 'string', 1, '阿里云平台access_key', NULL, NULL, NULL, NULL),
|
||||
(611, 'aliyun.sms.access_key_secret', '', 'string', 1, '阿里云平台access_key_secret', NULL, NULL, NULL, NULL),
|
||||
(612, 'aliyun.sms.sign_name', '', 'string', 1, '阿里云短信签名', NULL, NULL, NULL, NULL),
|
||||
(613, 'aliyun.sms.sms_code_template_code', '', 'string', 1, '阿里云短信模板', NULL, NULL, NULL, NULL);
|
||||
|
||||
update sys_params set remark = '是否允许管理员以外的人注册' where param_code = 'server.allow_user_register';
|
||||
|
||||
-- 增加手机区域字典
|
||||
-- 插入固件类型字典类型
|
||||
delete from `sys_dict_type` where `id` = 102;
|
||||
@@ -114,13 +114,6 @@ databaseChangeLog:
|
||||
- sqlFile:
|
||||
encoding: utf8
|
||||
path: classpath:db/changelog/202505091409.sql
|
||||
- changeSet:
|
||||
id: 202505141132
|
||||
author: zjy
|
||||
changes:
|
||||
- sqlFile:
|
||||
encoding: utf8
|
||||
path: classpath:db/changelog/202505141132.sql
|
||||
- changeSet:
|
||||
id: 202505091555
|
||||
author: whosmyqueen
|
||||
@@ -149,3 +142,10 @@ databaseChangeLog:
|
||||
- sqlFile:
|
||||
encoding: utf8
|
||||
path: classpath:db/changelog/202505142037.sql
|
||||
- changeSet:
|
||||
id: 202505182234
|
||||
author: amen
|
||||
changes:
|
||||
- sqlFile:
|
||||
encoding: utf8
|
||||
path: classpath:db/changelog/202505182234.sql
|
||||
@@ -1,23 +1,74 @@
|
||||
<template>
|
||||
<div class="copyright">
|
||||
©2025 xiaozhi-esp32-server v{{ version }}
|
||||
<div class="copyright">
|
||||
<div class="footer-content">
|
||||
<span>{{ year }} {{ name }} {{ version }}</span>
|
||||
<template v-if="beianGaNum !== 'null'">
|
||||
<span v-if="beianIcpNum !== 'null' || name">|</span>
|
||||
<a :href="'http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=' + beianGaNum" target="_blank"
|
||||
rel="noopener" class="beian-link">
|
||||
<img
|
||||
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAACXlBMVEUAAADax4rGxqP556zmsVD/6KXu5JvwvFPglEFnY3nnpEjosU1+XFTmuVfu13Hrv1jpsFa1k3mne2HlvnPdqmPqz3fu3YrJoWv04J/mz5z246aSi3i2p2/0zGj82WDrtFjar1O7klO4dkfimUJcWnbho0rgr1vntFfmvFflvGOZdFKXZkOUemnmv2XkuWWVe2ziqlXnt1Tr0HDpwl6ObmCkbEKqhVyOc2jdoVHmuljfrmDq0nPapWDUnErqy3XjtWXesXLrzHjz34rqzYfmw3fy4YrqyYDevW/dzoTIhkndsXLRsIneuHjlyIzQroTjuXjlyYfktWvw2prhvoHTmGfx45j3773TuYT/25LdijyKf321oXU+Nm3cumXfuWLtwlzpwVz40FvpvlvqvFfouFXjrlHipEjcmEfknEXPbzfbZyzZQR/YMBrWKBrhJhLdHxDTGg5HV4xBT4YYKYaWk4SDgYIWIYBybH0qLn1KS3tlZHpNS3WGfHIcHXEwLHAnF28rKW07NGuzn2pNQ2ruzWnDrGnuy2jryGjAnGgoH2j1x2RCNGPcrmIsEGGQdl/pu17YtV7FoV3muVxoRVzWqFvSmVp2SVbyw1XbkVTuvFPfo1PgmVPpt1LcnFLWkVLlp1F7W1HpsU92QE5oMUtFAUrpqUjnjUFOAEHUgz+DUj14FzvbczieMTdhATfodjXWcDXcbjXVcTS/Xi/hay7WXS3cWS3vYyvaVSrUVymnJSjXSyeKDSbNSiWfHCPMTyLFQiHkSR/lPxvVLRrPLBnUJRfUHhbaJxXVGxTZJxPNFhBdOhm/AAAAWXRSTlMABQIU/hIJ/v79/Pj29PPx4cC6s7CNfmxZWRv+/v7+/v7+/v78/Pr6+fPz8vHq6ujl5eTj4ODe2NTQzMW6ubKsnJeQkJCJiIOBgX9ubGpoW1lMREM0JR8dDgvYx1gAAAE7SURBVBjTYgADJmZJQUFJZiYGOGD2EzLV1jIT8paCibCJ86zctGPD1D4ecUaICKuPyYLMYznZOfNqzP0jwEJBOt1KB4/mHjqZ3dGsHwxW5M4Zk5l/PPdIQcGMShUPVqBlgQLlMSvyDq+P3HVidjWnkQQTA5MXV1SFYtb+jZGrl02si2J3Y2JgFMmILl68efueLVvXLSqLXirCCBgDow1HT+GS/AP7srblLS+K5bIHCjmodpXUr929d+eaVb3SsuouQCHhufPjShsbJk/rrK2KW8hiBxRyVkvnkI9tnaDQJDNFOU1DDOjRMEfe9Mjpiewz5RIzZmmKMoPcKqGbkJqSkBSfnDqpXS+ACeRpJ76W/uSUpDlpLPFtfK5soLAKFbU05Odm4eblN7YWk4KEGWOIp62FgYCVsG84SAAAL7BaooX965sAAAAASUVORK5CYII="
|
||||
class="beian-icon" alt="备案图标">
|
||||
<span class="beian-text">{{ beianGaNum }}</span>
|
||||
</a>
|
||||
</template>
|
||||
<template v-if="beianIcpNum !== 'null'">
|
||||
<span v-if="name">|</span>
|
||||
<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener" class="beian-link">
|
||||
<span class="beian-text">{{ beianIcpNum }}</span>
|
||||
</a>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: 'VersionFooter',
|
||||
computed: {
|
||||
...mapState({
|
||||
version: state => state.pubConfig.version
|
||||
})
|
||||
},
|
||||
mounted() {
|
||||
this.$store.dispatch('fetchPubConfig');
|
||||
}
|
||||
name: 'VersionFooter',
|
||||
computed: {
|
||||
...mapState({
|
||||
version: state => state.pubConfig.version,
|
||||
name: state => state.pubConfig.name,
|
||||
beianIcpNum: state => state.pubConfig.beianIcpNum,
|
||||
beianGaNum: state => state.pubConfig.beianGaNum,
|
||||
year: state => state.pubConfig.year
|
||||
})
|
||||
},
|
||||
mounted() {
|
||||
this.$store.dispatch('fetchPubConfig')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
<style scoped>
|
||||
.copyright {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.beian-link {
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.beian-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.beian-text {
|
||||
color: #000;
|
||||
font-size: 12px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
@@ -13,6 +13,8 @@ export default new Vuex.Store({
|
||||
isSuperAdmin: false, // 添加superAdmin状态
|
||||
pubConfig: { // 添加公共配置存储
|
||||
version: '',
|
||||
beianIcpNum: 'null',
|
||||
beianGaNum: 'null',
|
||||
allowUserRegister: false
|
||||
}
|
||||
},
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div class="operation-bar">
|
||||
<h2 class="page-title">参数管理</h2>
|
||||
<div class="right-operations">
|
||||
<el-input placeholder="请输入参数编码查询" v-model="searchCode" class="search-input"
|
||||
<el-input placeholder="请输入参数编码或备注查询" v-model="searchCode" class="search-input"
|
||||
@keyup.enter.native="handleSearch" clearable />
|
||||
<el-button class="btn-search" @click="handleSearch">搜索</el-button>
|
||||
</div>
|
||||
|
||||
@@ -411,7 +411,7 @@ LLM:
|
||||
base_url: https://host/api/v1
|
||||
# 你可以在这里找到你的api_key
|
||||
# https://cloud.tryfastgpt.ai/account/apikey
|
||||
api_key: fastgpt-xxx
|
||||
api_key: 你的fastgpt密钥
|
||||
variables:
|
||||
k: "v"
|
||||
k2: "v2"
|
||||
|
||||
@@ -82,6 +82,8 @@ def ensure_directories(config):
|
||||
|
||||
# ASR/TTS模块输出目录
|
||||
for module in ["ASR", "TTS"]:
|
||||
if config.get(module) is None:
|
||||
continue
|
||||
for provider in config.get(module, {}).values():
|
||||
output_dir = provider.get("output_dir", "")
|
||||
if output_dir:
|
||||
@@ -93,6 +95,10 @@ def ensure_directories(config):
|
||||
selected_provider = selected_modules.get(module_type)
|
||||
if not selected_provider:
|
||||
continue
|
||||
if config.get(module) is None:
|
||||
continue
|
||||
if config.get(selected_provider) is None:
|
||||
continue
|
||||
provider_config = config.get(module_type, {}).get(selected_provider, {})
|
||||
output_dir = provider_config.get("output_dir")
|
||||
if output_dir:
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import time
|
||||
import aiohttp
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
import statistics
|
||||
import time
|
||||
from typing import Dict
|
||||
|
||||
import aiohttp
|
||||
from tabulate import tabulate
|
||||
from typing import Dict, List
|
||||
|
||||
from config.settings import load_config
|
||||
from core.utils.asr import create_instance as create_stt_instance
|
||||
from core.utils.llm import create_instance as create_llm_instance
|
||||
from core.utils.tts import create_instance as create_tts_instance
|
||||
import statistics
|
||||
from config.settings import load_config
|
||||
import inspect
|
||||
import os
|
||||
import logging
|
||||
|
||||
# 设置全局日志级别为WARNING,抑制INFO级别日志
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
@@ -26,7 +28,17 @@ class AsyncPerformanceTester:
|
||||
"请用100字概括量子计算的基本原理和应用前景",
|
||||
],
|
||||
)
|
||||
self.results = {"llm": {}, "tts": {}, "combinations": []}
|
||||
|
||||
self.test_wav_list = []
|
||||
self.wav_root = r"config/assets"
|
||||
for file_name in os.listdir(self.wav_root):
|
||||
file_path = os.path.join(self.wav_root, file_name)
|
||||
# 检查文件大小是否大于300KB
|
||||
if os.path.getsize(file_path) > 300 * 1024: # 300KB = 300 * 1024 bytes
|
||||
with open(file_path, "rb") as f:
|
||||
self.test_wav_list.append(f.read())
|
||||
|
||||
self.results = {"llm": {}, "tts": {}, "stt": {}, "combinations": []}
|
||||
|
||||
async def _check_ollama_service(self, base_url: str, model_name: str) -> bool:
|
||||
"""异步检查Ollama服务状态"""
|
||||
@@ -109,6 +121,57 @@ class AsyncPerformanceTester:
|
||||
print(f"⚠️ {tts_name} 测试失败: {str(e)}")
|
||||
return {"name": tts_name, "type": "tts", "errors": 1}
|
||||
|
||||
async def _test_stt(self, stt_name: str, config: Dict) -> Dict:
|
||||
"""异步测试单个STT性能"""
|
||||
try:
|
||||
logging.getLogger("core.providers.asr.base").setLevel(logging.WARNING)
|
||||
token_fields = ["access_token", "api_key", "token"]
|
||||
if any(
|
||||
field in config
|
||||
and any(x in config[field] for x in ["你的", "placeholder"])
|
||||
for field in token_fields
|
||||
):
|
||||
print(f"⏭️ STT {stt_name} 未配置access_token/api_key,已跳过")
|
||||
return {"name": stt_name, "type": "stt", "errors": 1}
|
||||
|
||||
module_type = config.get("type", stt_name)
|
||||
stt = create_stt_instance(module_type, config, delete_audio_file=True)
|
||||
stt.audio_format = "pcm"
|
||||
|
||||
print(f"🎵 测试 STT: {stt_name}")
|
||||
|
||||
text, _ = await stt.speech_to_text([self.test_wav_list[0]], "1")
|
||||
|
||||
if text is None:
|
||||
print(f"❌ {stt_name} 连接失败")
|
||||
return {"name": stt_name, "type": "stt", "errors": 1}
|
||||
|
||||
total_time = 0
|
||||
test_count = len(self.test_wav_list)
|
||||
|
||||
for i, sentence in enumerate(self.test_wav_list, 1):
|
||||
start = time.time()
|
||||
text, _ = await stt.speech_to_text([sentence], "1")
|
||||
duration = time.time() - start
|
||||
total_time += duration
|
||||
|
||||
if text:
|
||||
print(f"✓ {stt_name} [{i}/{test_count}]")
|
||||
else:
|
||||
print(f"✗ {stt_name} [{i}/{test_count}]")
|
||||
return {"name": stt_name, "type": "stt", "errors": 1}
|
||||
|
||||
return {
|
||||
"name": stt_name,
|
||||
"type": "stt",
|
||||
"avg_time": total_time / test_count,
|
||||
"errors": 0,
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠️ {stt_name} 测试失败: {str(e)}")
|
||||
return {"name": stt_name, "type": "stt", "errors": 1}
|
||||
|
||||
async def _test_llm(self, llm_name: str, config: Dict) -> Dict:
|
||||
"""异步测试单个LLM性能"""
|
||||
try:
|
||||
@@ -234,6 +297,7 @@ class AsyncPerformanceTester:
|
||||
if v["errors"] == 0 and v["avg_first_token"] >= 0.05
|
||||
]
|
||||
valid_tts = [k for k, v in self.results["tts"].items() if v["errors"] == 0]
|
||||
valid_stt = [k for k, v in self.results["stt"].items() if v["errors"] == 0]
|
||||
|
||||
# 找出基准值
|
||||
min_first_token = (
|
||||
@@ -246,42 +310,53 @@ class AsyncPerformanceTester:
|
||||
if valid_tts
|
||||
else 1
|
||||
)
|
||||
min_stt_time = (
|
||||
min([self.results["stt"][stt]["avg_time"] for stt in valid_stt])
|
||||
if valid_stt
|
||||
else 1
|
||||
)
|
||||
|
||||
for llm in valid_llms:
|
||||
for tts in valid_tts:
|
||||
# 计算相对性能分数(越小越好)
|
||||
llm_score = (
|
||||
self.results["llm"][llm]["avg_first_token"] / min_first_token
|
||||
)
|
||||
tts_score = self.results["tts"][tts]["avg_time"] / min_tts_time
|
||||
for stt in valid_stt:
|
||||
# 计算相对性能分数(越小越好)
|
||||
llm_score = (
|
||||
self.results["llm"][llm]["avg_first_token"] / min_first_token
|
||||
)
|
||||
tts_score = self.results["tts"][tts]["avg_time"] / min_tts_time
|
||||
stt_score = self.results["stt"][stt]["avg_time"] / min_stt_time
|
||||
|
||||
# 计算稳定性分数(标准差/平均值,越小越稳定)
|
||||
llm_stability = (
|
||||
self.results["llm"][llm]["std_first_token"]
|
||||
/ self.results["llm"][llm]["avg_first_token"]
|
||||
)
|
||||
# 计算稳定性分数(标准差/平均值,越小越稳定)
|
||||
llm_stability = (
|
||||
self.results["llm"][llm]["std_first_token"]
|
||||
/ self.results["llm"][llm]["avg_first_token"]
|
||||
)
|
||||
|
||||
# 综合得分(考虑性能和稳定性)
|
||||
# 性能权重0.7,稳定性权重0.3
|
||||
llm_final_score = llm_score * 0.7 + llm_stability * 0.3
|
||||
# 综合得分(考虑性能和稳定性)
|
||||
# LLM得分: 性能权重(70%) + 稳定性权重(30%)
|
||||
llm_final_score = llm_score * 0.7 + llm_stability * 0.3
|
||||
|
||||
# 总分 = LLM得分(70%) + TTS得分(30%)
|
||||
total_score = llm_final_score * 0.7 + tts_score * 0.3
|
||||
# 总分 = LLM得分(70%) + TTS得分(30%) + STT得分(30%)
|
||||
total_score = (
|
||||
llm_final_score * 0.7 + tts_score * 0.3 + stt_score * 0.3
|
||||
)
|
||||
|
||||
self.results["combinations"].append(
|
||||
{
|
||||
"llm": llm,
|
||||
"tts": tts,
|
||||
"score": total_score,
|
||||
"details": {
|
||||
"llm_first_token": self.results["llm"][llm][
|
||||
"avg_first_token"
|
||||
],
|
||||
"llm_stability": llm_stability,
|
||||
"tts_time": self.results["tts"][tts]["avg_time"],
|
||||
},
|
||||
}
|
||||
)
|
||||
self.results["combinations"].append(
|
||||
{
|
||||
"llm": llm,
|
||||
"tts": tts,
|
||||
"stt": stt,
|
||||
"score": total_score,
|
||||
"details": {
|
||||
"llm_first_token": self.results["llm"][llm][
|
||||
"avg_first_token"
|
||||
],
|
||||
"llm_stability": llm_stability,
|
||||
"tts_time": self.results["tts"][tts]["avg_time"],
|
||||
"stt_time": self.results["stt"][stt]["avg_time"],
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
# 分数越小越好
|
||||
self.results["combinations"].sort(key=lambda x: x["score"])
|
||||
@@ -302,7 +377,7 @@ class AsyncPerformanceTester:
|
||||
)
|
||||
|
||||
if llm_table:
|
||||
print("\nLLM 性能排行:")
|
||||
print("\nLLM 性能排行:\n")
|
||||
print(
|
||||
tabulate(
|
||||
llm_table,
|
||||
@@ -321,7 +396,7 @@ class AsyncPerformanceTester:
|
||||
tts_table.append([name, f"{data['avg_time']:.3f}秒"]) # 不需要固定宽度
|
||||
|
||||
if tts_table:
|
||||
print("\nTTS 性能排行:")
|
||||
print("\nTTS 性能排行:\n")
|
||||
print(
|
||||
tabulate(
|
||||
tts_table,
|
||||
@@ -334,17 +409,37 @@ class AsyncPerformanceTester:
|
||||
else:
|
||||
print("\n⚠️ 没有可用的TTS模块进行测试。")
|
||||
|
||||
stt_table = []
|
||||
for name, data in self.results["stt"].items():
|
||||
if data["errors"] == 0:
|
||||
stt_table.append([name, f"{data['avg_time']:.3f}秒"]) # 不需要固定宽度
|
||||
|
||||
if stt_table:
|
||||
print("\nSTT 性能排行:\n")
|
||||
print(
|
||||
tabulate(
|
||||
stt_table,
|
||||
headers=["模型名称", "合成耗时"],
|
||||
tablefmt="github",
|
||||
colalign=("left", "right"),
|
||||
disable_numparse=True,
|
||||
)
|
||||
)
|
||||
else:
|
||||
print("\n⚠️ 没有可用的STT模块进行测试。")
|
||||
|
||||
if self.results["combinations"]:
|
||||
print("\n推荐配置组合 (得分越小越好):")
|
||||
print("\n推荐配置组合 (得分越小越好):\n")
|
||||
combo_table = []
|
||||
for combo in self.results["combinations"][:5]:
|
||||
for combo in self.results["combinations"][:]:
|
||||
combo_table.append(
|
||||
[
|
||||
f"{combo['llm']} + {combo['tts']}", # 不需要固定宽度
|
||||
f"{combo['llm']} + {combo['tts']} + {combo['stt']}", # 不需要固定宽度
|
||||
f"{combo['score']:.3f}",
|
||||
f"{combo['details']['llm_first_token']:.3f}秒",
|
||||
f"{combo['details']['llm_stability']:.3f}",
|
||||
f"{combo['details']['tts_time']:.3f}秒",
|
||||
f"{combo['details']['stt_time']:.3f}秒",
|
||||
]
|
||||
)
|
||||
|
||||
@@ -357,9 +452,10 @@ class AsyncPerformanceTester:
|
||||
"LLM首字耗时",
|
||||
"稳定性",
|
||||
"TTS合成耗时",
|
||||
"STT合成耗时",
|
||||
],
|
||||
tablefmt="github",
|
||||
colalign=("left", "right", "right", "right", "right"),
|
||||
colalign=("left", "right", "right", "right", "right", "right"),
|
||||
disable_numparse=True,
|
||||
)
|
||||
)
|
||||
@@ -372,8 +468,12 @@ class AsyncPerformanceTester:
|
||||
if result["errors"] == 0:
|
||||
if result["type"] == "llm":
|
||||
self.results["llm"][result["name"]] = result
|
||||
else:
|
||||
elif result["type"] == "tts":
|
||||
self.results["tts"][result["name"]] = result
|
||||
elif result["type"] == "stt":
|
||||
self.results["stt"][result["name"]] = result
|
||||
else:
|
||||
pass
|
||||
|
||||
async def run(self):
|
||||
"""执行全量异步测试"""
|
||||
@@ -383,52 +483,73 @@ class AsyncPerformanceTester:
|
||||
all_tasks = []
|
||||
|
||||
# LLM测试任务
|
||||
for llm_name, config in self.config.get("LLM", {}).items():
|
||||
# 检查配置有效性
|
||||
if llm_name == "CozeLLM":
|
||||
if any(x in config.get("bot_id", "") for x in ["你的"]) or any(
|
||||
x in config.get("user_id", "") for x in ["你的"]
|
||||
if self.config.get("LLM") is not None:
|
||||
for llm_name, config in self.config.get("LLM", {}).items():
|
||||
# 检查配置有效性
|
||||
if llm_name == "CozeLLM":
|
||||
if any(x in config.get("bot_id", "") for x in ["你的"]) or any(
|
||||
x in config.get("user_id", "") for x in ["你的"]
|
||||
):
|
||||
print(f"⏭️ LLM {llm_name} 未配置bot_id/user_id,已跳过")
|
||||
continue
|
||||
elif "api_key" in config and any(
|
||||
x in config["api_key"] for x in ["你的", "placeholder", "sk-xxx"]
|
||||
):
|
||||
print(f"⏭️ LLM {llm_name} 未配置bot_id/user_id,已跳过")
|
||||
continue
|
||||
elif "api_key" in config and any(
|
||||
x in config["api_key"] for x in ["你的", "placeholder", "sk-xxx"]
|
||||
):
|
||||
print(f"⏭️ LLM {llm_name} 未配置api_key,已跳过")
|
||||
continue
|
||||
|
||||
# 对于Ollama,先检查服务状态
|
||||
if llm_name == "Ollama":
|
||||
base_url = config.get("base_url", "http://localhost:11434")
|
||||
model_name = config.get("model_name")
|
||||
if not model_name:
|
||||
print(f"🚫 Ollama未配置model_name")
|
||||
print(f"⏭️ LLM {llm_name} 未配置api_key,已跳过")
|
||||
continue
|
||||
|
||||
if not await self._check_ollama_service(base_url, model_name):
|
||||
continue
|
||||
# 对于Ollama,先检查服务状态
|
||||
if llm_name == "Ollama":
|
||||
base_url = config.get("base_url", "http://localhost:11434")
|
||||
model_name = config.get("model_name")
|
||||
if not model_name:
|
||||
print(f"🚫 Ollama未配置model_name")
|
||||
continue
|
||||
|
||||
print(f"📋 添加LLM测试任务: {llm_name}")
|
||||
module_type = config.get("type", llm_name)
|
||||
llm = create_llm_instance(module_type, config)
|
||||
if not await self._check_ollama_service(base_url, model_name):
|
||||
continue
|
||||
|
||||
# 为每个句子创建独立任务
|
||||
for sentence in self.test_sentences:
|
||||
sentence = sentence.encode("utf-8").decode("utf-8")
|
||||
all_tasks.append(self._test_single_sentence(llm_name, llm, sentence))
|
||||
print(f"📋 添加LLM测试任务: {llm_name}")
|
||||
module_type = config.get("type", llm_name)
|
||||
llm = create_llm_instance(module_type, config)
|
||||
|
||||
# 为每个句子创建独立任务
|
||||
for sentence in self.test_sentences:
|
||||
sentence = sentence.encode("utf-8").decode("utf-8")
|
||||
all_tasks.append(
|
||||
self._test_single_sentence(llm_name, llm, sentence)
|
||||
)
|
||||
|
||||
# TTS测试任务
|
||||
for tts_name, config in self.config.get("TTS", {}).items():
|
||||
token_fields = ["access_token", "api_key", "token"]
|
||||
if any(
|
||||
field in config
|
||||
and any(x in config[field] for x in ["你的", "placeholder"])
|
||||
for field in token_fields
|
||||
):
|
||||
print(f"⏭️ TTS {tts_name} 未配置access_token/api_key,已跳过")
|
||||
continue
|
||||
print(f"🎵 添加TTS测试任务: {tts_name}")
|
||||
all_tasks.append(self._test_tts(tts_name, config))
|
||||
if self.config.get("TTS") is not None:
|
||||
for tts_name, config in self.config.get("TTS", {}).items():
|
||||
token_fields = ["access_token", "api_key", "token"]
|
||||
if any(
|
||||
field in config
|
||||
and any(x in config[field] for x in ["你的", "placeholder"])
|
||||
for field in token_fields
|
||||
):
|
||||
print(f"⏭️ TTS {tts_name} 未配置access_token/api_key,已跳过")
|
||||
continue
|
||||
print(f"🎵 添加TTS测试任务: {tts_name}")
|
||||
all_tasks.append(self._test_tts(tts_name, config))
|
||||
|
||||
# STT测试任务
|
||||
if len(self.test_wav_list) >= 1:
|
||||
if self.config.get("ASR") is not None:
|
||||
for stt_name, config in self.config.get("ASR", {}).items():
|
||||
token_fields = ["access_token", "api_key", "token"]
|
||||
if any(
|
||||
field in config
|
||||
and any(x in config[field] for x in ["你的", "placeholder"])
|
||||
for field in token_fields
|
||||
):
|
||||
print(f"⏭️ ASR {stt_name} 未配置access_token/api_key,已跳过")
|
||||
continue
|
||||
print(f"🎵 添加ASR测试任务: {stt_name}")
|
||||
all_tasks.append(self._test_stt(stt_name, config))
|
||||
else:
|
||||
print(f"\n⚠️ {self.wav_root} 路径下没有音频文件,已跳过STT测试任务")
|
||||
|
||||
print(
|
||||
f"\n✅ 找到 {len([t for t in all_tasks if 'test_single_sentence' in str(t)]) / len(self.test_sentences):.0f} 个可用LLM模块"
|
||||
@@ -436,6 +557,9 @@ class AsyncPerformanceTester:
|
||||
print(
|
||||
f"✅ 找到 {len([t for t in all_tasks if '_test_tts' in str(t)])} 个可用TTS模块"
|
||||
)
|
||||
print(
|
||||
f"✅ 找到 {len([t for t in all_tasks if '_test_stt' in str(t)])} 个可用STT模块"
|
||||
)
|
||||
print("\n⏳ 开始并发测试所有模块...\n")
|
||||
|
||||
# 并发执行所有测试任务
|
||||
@@ -494,6 +618,15 @@ class AsyncPerformanceTester:
|
||||
if result["errors"] == 0:
|
||||
self.results["tts"][result["name"]] = result
|
||||
|
||||
# 处理STT结果
|
||||
for result in [
|
||||
r
|
||||
for r in all_results
|
||||
if r and isinstance(r, dict) and r.get("type") == "stt"
|
||||
]:
|
||||
if result["errors"] == 0:
|
||||
self.results["stt"][result["name"]] = result
|
||||
|
||||
# 生成组合建议并打印结果
|
||||
print("\n📊 生成测试报告...")
|
||||
self._generate_combinations()
|
||||
|
||||
Reference in New Issue
Block a user