mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-22 07:03:53 +08:00
Merge branch 'main' of https://github.com/xinnan-tech/xiaozhi-esp32-server into new
This commit is contained in:
@@ -43,7 +43,7 @@ public class SwaggerConfig {
|
||||
public GroupedOpenApi oatApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("ota")
|
||||
.pathsToMatch("/ota/**")
|
||||
.pathsToMatch("/ota/**", "/otaMag/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -240,4 +240,9 @@ public interface ErrorCode {
|
||||
int RAG_ADAPTER_CACHE_ERROR = 10191; // 适配器缓存错误
|
||||
int RAG_ADAPTER_TYPE_NOT_FOUND = 10192; // 适配器类型未找到
|
||||
|
||||
// 设备工具相关错误码
|
||||
int DEVICE_ID_NOT_NULL = 10193; // 设备ID不能为空
|
||||
int DEVICE_NOT_EXIST = 10194; // 设备不存在
|
||||
int OTA_UPLOAD_COUNT_EXCEED = 10195; // OTA上传次数超过限制
|
||||
|
||||
}
|
||||
|
||||
@@ -170,10 +170,21 @@ public class RedisKeys {
|
||||
/**
|
||||
* OTA绑定设备
|
||||
*/
|
||||
public static String getOtaActivationCode(String activationCode) {return "ota:activation:code:" + activationCode;}
|
||||
public static String getOtaActivationCode(String activationCode) {
|
||||
return "ota:activation:code:" + activationCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* OTA获取设备mac相关信息
|
||||
*/
|
||||
public static String getOtaDeviceActivationInfo(String deviceId) {return "ota:activation:data:" + deviceId;}
|
||||
public static String getOtaDeviceActivationInfo(String deviceId) {
|
||||
return "ota:activation:data:" + deviceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* OTA上传次数
|
||||
*/
|
||||
public static String getOtaUploadCountKey(Long username) {
|
||||
return "ota:upload:count:" + username;
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -147,6 +147,7 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
|
||||
List<String> result = toolsList.stream()
|
||||
.map(tool -> (String) tool.get("name"))
|
||||
.filter(name -> name != null)
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
log.info("成功获取MCP工具列表,智能体ID: {}, 工具数量: {}", id, result.size());
|
||||
return result;
|
||||
|
||||
+12
-5
@@ -1,6 +1,12 @@
|
||||
package xiaozhi.modules.agent.service.impl;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -81,9 +87,9 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
|
||||
|
||||
if (agent.getMemModelId() != null && agent.getMemModelId().equals(Constant.MEMORY_NO_MEM)) {
|
||||
agent.setChatHistoryConf(Constant.ChatHistoryConfEnum.IGNORE.getCode());
|
||||
if (agent.getChatHistoryConf() == null) {
|
||||
agent.setChatHistoryConf(Constant.ChatHistoryConfEnum.RECORD_TEXT_AUDIO.getCode());
|
||||
}
|
||||
}
|
||||
if (agent.getChatHistoryConf() == null) {
|
||||
agent.setChatHistoryConf(Constant.ChatHistoryConfEnum.RECORD_TEXT_AUDIO.getCode());
|
||||
}
|
||||
|
||||
// 查询上下文源配置
|
||||
@@ -132,7 +138,8 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
|
||||
if (StringUtils.isNotBlank(keyword)) {
|
||||
if ("mac".equals(searchType)) {
|
||||
// 按MAC地址搜索:先搜索设备,再获取对应的智能体
|
||||
List<DeviceEntity> devices = Optional.ofNullable(deviceService.searchDevicesByMacAddress(keyword, userId)).orElseGet(ArrayList::new);
|
||||
List<DeviceEntity> devices = Optional
|
||||
.ofNullable(deviceService.searchDevicesByMacAddress(keyword, userId)).orElseGet(ArrayList::new);
|
||||
// 获取设备对应的智能体ID列表
|
||||
List<String> agentIds = devices.stream()
|
||||
.map(DeviceEntity::getAgentId)
|
||||
|
||||
+35
-1
@@ -1,6 +1,8 @@
|
||||
package xiaozhi.modules.device.controller;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
@@ -11,6 +13,7 @@ import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
@@ -21,6 +24,7 @@ import xiaozhi.common.user.UserDetail;
|
||||
import xiaozhi.common.utils.Result;
|
||||
import xiaozhi.modules.device.dto.DeviceManualAddDTO;
|
||||
import xiaozhi.modules.device.dto.DeviceRegisterDTO;
|
||||
import xiaozhi.modules.device.dto.DeviceToolsCallReqDTO;
|
||||
import xiaozhi.modules.device.dto.DeviceUnBindDTO;
|
||||
import xiaozhi.modules.device.dto.DeviceUpdateDTO;
|
||||
import xiaozhi.modules.device.entity.DeviceEntity;
|
||||
@@ -125,4 +129,34 @@ public class DeviceController {
|
||||
deviceService.manualAddDevice(user.getId(), dto);
|
||||
return new Result<>();
|
||||
}
|
||||
|
||||
@PostMapping("/tools/list/{deviceId}")
|
||||
@Operation(summary = "获取设备工具列表")
|
||||
@RequiresPermissions("sys:role:normal")
|
||||
public Result<Object> getDeviceTools(@PathVariable String deviceId) {
|
||||
Object toolsData = deviceService.getDeviceTools(deviceId);
|
||||
if (toolsData == null) {
|
||||
return new Result<Object>().error(ErrorCode.DEVICE_NOT_EXIST);
|
||||
}
|
||||
|
||||
return new Result<Object>().ok(toolsData);
|
||||
}
|
||||
|
||||
@PostMapping("/tools/call/{deviceId}")
|
||||
@Operation(summary = "调用设备工具")
|
||||
@RequiresPermissions("sys:role:normal")
|
||||
public Result<Object> callDeviceTool(@PathVariable String deviceId,
|
||||
@Valid @RequestBody DeviceToolsCallReqDTO request) {
|
||||
String toolName = request.getName();
|
||||
Map<String, Object> arguments = request.getArguments();
|
||||
|
||||
Object result = deviceService.callDeviceTool(deviceId, toolName, arguments);
|
||||
if (result == null) {
|
||||
return new Result<Object>().error(ErrorCode.DEVICE_NOT_EXIST);
|
||||
}
|
||||
|
||||
Result<Object> response = new Result<Object>();
|
||||
response.setMsg("Tools called successfully");
|
||||
return response.ok(result);
|
||||
}
|
||||
}
|
||||
+55
-3
@@ -38,6 +38,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import xiaozhi.common.constant.Constant;
|
||||
import xiaozhi.common.exception.ErrorCode;
|
||||
import xiaozhi.common.page.PageData;
|
||||
import xiaozhi.common.redis.RedisKeys;
|
||||
import xiaozhi.common.redis.RedisUtils;
|
||||
@@ -45,8 +46,11 @@ import xiaozhi.common.utils.Result;
|
||||
import xiaozhi.common.validator.ValidatorUtils;
|
||||
import xiaozhi.modules.device.entity.OtaEntity;
|
||||
import xiaozhi.modules.device.service.OtaService;
|
||||
import xiaozhi.modules.security.user.SecurityUser;
|
||||
import xiaozhi.modules.sys.enums.SuperAdminEnum;
|
||||
import xiaozhi.modules.sys.service.SysParamsService;
|
||||
|
||||
@Tag(name = "设备管理", description = "OTA 相关接口")
|
||||
@Tag(name = "固件升级管理", description = "OTA 相关接口")
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@@ -55,6 +59,7 @@ public class OTAMagController {
|
||||
private static final Logger logger = LoggerFactory.getLogger(OTAController.class);
|
||||
private final OtaService otaService;
|
||||
private final RedisUtils redisUtils;
|
||||
private final SysParamsService sysParamsService;
|
||||
|
||||
@GetMapping
|
||||
@Operation(summary = "分页查询 OTA 固件信息")
|
||||
@@ -160,7 +165,17 @@ public class OTAMagController {
|
||||
|
||||
try {
|
||||
// 获取固件信息
|
||||
OtaEntity otaEntity = otaService.selectById(id);
|
||||
OtaEntity otaEntity = null;
|
||||
if (id.indexOf("file:") == 0) {
|
||||
id = id.substring(5);
|
||||
otaEntity = new OtaEntity();
|
||||
otaEntity.setFirmwarePath(id);
|
||||
otaEntity.setType("assets");
|
||||
otaEntity.setVersion("1.0.0");
|
||||
} else {
|
||||
otaEntity = otaService.selectById(id);
|
||||
}
|
||||
|
||||
if (otaEntity == null || StringUtils.isBlank(otaEntity.getFirmwarePath())) {
|
||||
logger.warn("Firmware not found or path is empty for ID: {}", id);
|
||||
return ResponseEntity.notFound().build();
|
||||
@@ -168,6 +183,7 @@ public class OTAMagController {
|
||||
|
||||
// 获取文件路径 - 确保路径是绝对路径或正确的相对路径
|
||||
String firmwarePath = otaEntity.getFirmwarePath();
|
||||
String originalFilename = otaEntity.getType() + "_" + otaEntity.getVersion();
|
||||
Path path;
|
||||
|
||||
// 检查是否是绝对路径
|
||||
@@ -201,7 +217,7 @@ public class OTAMagController {
|
||||
byte[] fileContent = Files.readAllBytes(path);
|
||||
|
||||
// 设置响应头
|
||||
String originalFilename = otaEntity.getType() + "_" + otaEntity.getVersion();
|
||||
|
||||
if (firmwarePath.contains(".")) {
|
||||
String extension = firmwarePath.substring(firmwarePath.lastIndexOf("."));
|
||||
originalFilename += extension;
|
||||
@@ -277,6 +293,42 @@ public class OTAMagController {
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/uploadAssetsBin")
|
||||
@Operation(summary = "上传资源固件文件")
|
||||
@RequiresPermissions("sys:role:normal")
|
||||
public Result<String> uploadAssetsBin(@RequestParam("file") MultipartFile file) {
|
||||
String otaUrl = sysParamsService.getValue(Constant.SERVER_OTA, true);
|
||||
if (StringUtils.isBlank(otaUrl) || otaUrl.equals("null")) {
|
||||
return new Result<String>().error(ErrorCode.OTA_URL_EMPTY);
|
||||
}
|
||||
logger.info("username:{},uploadAssetsBin size: {}", SecurityUser.getUser().getUsername(), file.getSize());
|
||||
// 验证文件大小 (资源固件最大20MB)
|
||||
if (file.getSize() > 20 * 1024 * 1024) {
|
||||
return new Result<String>().error(ErrorCode.VOICE_CLONE_AUDIO_TOO_LARGE);
|
||||
}
|
||||
// 普通用户只能每天上传50次
|
||||
if (SecurityUser.getUser().getSuperAdmin() == SuperAdminEnum.NO.value()) {
|
||||
String uploadCountKey = RedisKeys.getOtaUploadCountKey(SecurityUser.getUser().getId());
|
||||
Integer uploadCount = (Integer) Optional.ofNullable(redisUtils.get(uploadCountKey)).orElse(0);
|
||||
if (uploadCount >= 50) {
|
||||
return new Result<String>().error(ErrorCode.OTA_UPLOAD_COUNT_EXCEED);
|
||||
}
|
||||
// 增加上传次数
|
||||
redisUtils.increment(RedisKeys.getOtaUploadCountKey(SecurityUser.getUser().getId()),
|
||||
RedisUtils.DEFAULT_EXPIRE);
|
||||
}
|
||||
Result<String> result = uploadFirmware(file);
|
||||
|
||||
// 生成资源文件路径
|
||||
if (StringUtils.isNotBlank(result.getData())) {
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
redisUtils.set(RedisKeys.getOtaIdKey(uuid), "file:" + result.getData());
|
||||
String downloadUrl = otaUrl.replace("/ota/", "/otaMag/download/") + uuid;
|
||||
result.setData(downloadUrl);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String calculateMD5(MultipartFile file) throws IOException, NoSuchAlgorithmException {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
byte[] digest = md.digest(file.getBytes());
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package xiaozhi.modules.device.dto;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class DeviceToolsCallReqDTO {
|
||||
|
||||
@NotBlank(message = "工具名称不能为空")
|
||||
private String name;
|
||||
|
||||
private Map<String, Object> arguments;
|
||||
}
|
||||
@@ -2,13 +2,14 @@ package xiaozhi.modules.device.service;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import xiaozhi.common.page.PageData;
|
||||
import xiaozhi.common.service.BaseService;
|
||||
import xiaozhi.modules.device.dto.DeviceManualAddDTO;
|
||||
import xiaozhi.modules.device.dto.DevicePageUserDTO;
|
||||
import xiaozhi.modules.device.dto.DeviceReportReqDTO;
|
||||
import xiaozhi.modules.device.dto.DeviceReportRespDTO;
|
||||
import xiaozhi.modules.device.dto.DeviceManualAddDTO;
|
||||
import xiaozhi.modules.device.entity.DeviceEntity;
|
||||
import xiaozhi.modules.device.vo.UserShowDeviceListVO;
|
||||
|
||||
@@ -87,6 +88,7 @@ public interface DeviceService extends BaseService<DeviceEntity> {
|
||||
|
||||
/**
|
||||
* 获取这个智能体设备理的最近的最后连接时间
|
||||
*
|
||||
* @param agentId 智能体id
|
||||
* @return 返回设备最近的最后连接时间
|
||||
*/
|
||||
@@ -116,9 +118,19 @@ public interface DeviceService extends BaseService<DeviceEntity> {
|
||||
* 根据MAC地址搜索设备
|
||||
*
|
||||
* @param macAddress MAC地址关键词
|
||||
* @param userId 用户ID
|
||||
* @param userId 用户ID
|
||||
* @return 设备列表
|
||||
*/
|
||||
List<DeviceEntity> searchDevicesByMacAddress(String macAddress, Long userId);
|
||||
|
||||
/**
|
||||
* 获取设备工具列表
|
||||
*/
|
||||
Object getDeviceTools(String deviceId);
|
||||
|
||||
/**
|
||||
* 调用设备工具
|
||||
*/
|
||||
Object callDeviceTool(String deviceId, String toolName, Map<String, Object> arguments);
|
||||
|
||||
}
|
||||
+176
-14
@@ -4,21 +4,20 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.Base64;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.digest.DigestUtil;
|
||||
import cn.hutool.http.ContentType;
|
||||
import cn.hutool.http.Header;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.aop.framework.AopContext;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
@@ -30,7 +29,16 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.digest.DigestUtil;
|
||||
import cn.hutool.http.ContentType;
|
||||
import cn.hutool.http.Header;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -44,7 +52,6 @@ import xiaozhi.common.service.impl.BaseServiceImpl;
|
||||
import xiaozhi.common.user.UserDetail;
|
||||
import xiaozhi.common.utils.ConvertUtils;
|
||||
import xiaozhi.common.utils.DateUtils;
|
||||
import xiaozhi.common.utils.Result;
|
||||
import xiaozhi.common.utils.ToolUtil;
|
||||
import xiaozhi.modules.device.dao.DeviceDao;
|
||||
import xiaozhi.modules.device.dto.DeviceManualAddDTO;
|
||||
@@ -179,7 +186,7 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
||||
.header(Header.CONTENT_TYPE, ContentType.JSON.getValue())
|
||||
.header(Header.AUTHORIZATION, "Bearer " + generateBearerToken())
|
||||
.body(JSONUtil.toJsonStr(params))
|
||||
.timeout(10000) //超时,毫秒
|
||||
.timeout(10000) // 超时,毫秒
|
||||
.execute().body();
|
||||
return resultMessage;
|
||||
}
|
||||
@@ -649,14 +656,13 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
||||
return mqtt;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成BearerToken
|
||||
*/
|
||||
private String generateBearerToken() {
|
||||
try {
|
||||
String dateStr = DateUtil.format(new Date(), DatePattern.NORM_DATE_PATTERN);
|
||||
String signatureKey = sysParamsService.getValue(Constant.SERVER_MQTT_SECRET,false);
|
||||
String signatureKey = sysParamsService.getValue(Constant.SERVER_MQTT_SECRET, false);
|
||||
if (ToolUtil.isEmpty(signatureKey)) {
|
||||
return null;
|
||||
}
|
||||
@@ -665,4 +671,160 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getDeviceTools(String deviceId) {
|
||||
// 从系统参数中获取MQTT网关地址
|
||||
String mqttGatewayUrl = sysParamsService.getValue("server.mqtt_manager_api", true);
|
||||
if (StringUtils.isBlank(mqttGatewayUrl) || "null".equals(mqttGatewayUrl)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取设备信息
|
||||
DeviceEntity device = baseDao.selectById(deviceId);
|
||||
if (device == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 检查设备是否属于当前用户
|
||||
UserDetail user = SecurityUser.getUser();
|
||||
if (!device.getUserId().equals(user.getId())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 构建clientId
|
||||
String macAddress = Optional.ofNullable(device.getMacAddress()).orElse("unknown").replace(":", "_");
|
||||
String groupId = Optional.ofNullable(device.getBoard()).orElse("GID_default").replace(":", "_");
|
||||
String clientId = StrUtil.format("{}@@@{}@@@{}", groupId, macAddress, macAddress);
|
||||
|
||||
// 构建完整的URL
|
||||
String url = StrUtil.format("http://{}/api/commands/{}", mqttGatewayUrl, clientId);
|
||||
|
||||
// 构建请求体
|
||||
Map<String, Object> payload = MapUtil
|
||||
.builder(new HashMap<String, Object>())
|
||||
.put("jsonrpc", "2.0")
|
||||
.put("id", 2)
|
||||
.put("method", "tools/list")
|
||||
.put("params", MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("withUserTools", true)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
Map<String, Object> requestBody = MapUtil
|
||||
.builder(new HashMap<String, Object>())
|
||||
.put("type", "mcp")
|
||||
.put("payload", payload)
|
||||
.build();
|
||||
|
||||
// 发送请求
|
||||
String resultMessage = HttpRequest.post(url)
|
||||
.header(Header.CONTENT_TYPE, ContentType.JSON.getValue())
|
||||
.header(Header.AUTHORIZATION, "Bearer " + generateBearerToken())
|
||||
.body(JSONUtil.toJsonStr(requestBody))
|
||||
.timeout(10000) // 超时,毫秒
|
||||
.execute().body();
|
||||
|
||||
// 解析响应
|
||||
if (StringUtils.isNotBlank(resultMessage)) {
|
||||
cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(resultMessage);
|
||||
if (jsonObject.getBool("success", false)) {
|
||||
return jsonObject.get("data");
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object callDeviceTool(String deviceId, String toolName, Map<String, Object> arguments) {
|
||||
// 从系统参数中获取MQTT网关地址
|
||||
String mqttGatewayUrl = sysParamsService.getValue("server.mqtt_manager_api", true);
|
||||
if (StringUtils.isBlank(mqttGatewayUrl) || "null".equals(mqttGatewayUrl)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取设备信息
|
||||
DeviceEntity device = baseDao.selectById(deviceId);
|
||||
if (device == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 检查设备是否属于当前用户
|
||||
UserDetail user = SecurityUser.getUser();
|
||||
if (!device.getUserId().equals(user.getId())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 构建clientId
|
||||
String macAddress = Optional.ofNullable(device.getMacAddress()).orElse("unknown").replace(":", "_");
|
||||
String groupId = Optional.ofNullable(device.getBoard()).orElse("GID_default").replace(":", "_");
|
||||
String clientId = StrUtil.format("{}@@@{}@@@{}", groupId, macAddress, macAddress);
|
||||
|
||||
// 构建完整的URL
|
||||
String url = StrUtil.format("http://{}/api/commands/{}", mqttGatewayUrl, clientId);
|
||||
|
||||
// 构建请求体
|
||||
Map<String, Object> params = MapUtil
|
||||
.builder(new HashMap<String, Object>())
|
||||
.put("name", toolName)
|
||||
.put("arguments", arguments)
|
||||
.build();
|
||||
|
||||
Map<String, Object> payload = MapUtil
|
||||
.builder(new HashMap<String, Object>())
|
||||
.put("jsonrpc", "2.0")
|
||||
.put("id", 2)
|
||||
.put("method", "tools/call")
|
||||
.put("params", params)
|
||||
.build();
|
||||
|
||||
Map<String, Object> requestBody = MapUtil
|
||||
.builder(new HashMap<String, Object>())
|
||||
.put("type", "mcp")
|
||||
.put("payload", payload)
|
||||
.build();
|
||||
|
||||
// 发送请求
|
||||
String resultMessage = HttpRequest.post(url)
|
||||
.header(Header.CONTENT_TYPE, ContentType.JSON.getValue())
|
||||
.header(Header.AUTHORIZATION, "Bearer " + generateBearerToken())
|
||||
.body(JSONUtil.toJsonStr(requestBody))
|
||||
.timeout(10000) // 超时,毫秒
|
||||
.execute().body();
|
||||
|
||||
// 解析响应
|
||||
if (StringUtils.isNotBlank(resultMessage)) {
|
||||
cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(resultMessage);
|
||||
if (jsonObject.getBool("success", false)) {
|
||||
cn.hutool.json.JSONObject data = jsonObject.getJSONObject("data");
|
||||
if (data != null) {
|
||||
cn.hutool.json.JSONArray content = data.getJSONArray("content");
|
||||
if (content != null && content.size() > 0) {
|
||||
cn.hutool.json.JSONObject firstContent = content.getJSONObject(0);
|
||||
if (firstContent != null && "text".equals(firstContent.getStr("type"))) {
|
||||
String text = firstContent.getStr("text");
|
||||
if (StringUtils.isNotBlank(text)) {
|
||||
String trimmedText = text.trim();
|
||||
if (trimmedText.startsWith("{") || trimmedText.startsWith("[")) {
|
||||
try {
|
||||
return JSONUtil.parseObj(trimmedText);
|
||||
} catch (Exception e) {
|
||||
return trimmedText;
|
||||
}
|
||||
} else if ("true".equals(trimmedText)) {
|
||||
return true;
|
||||
} else if ("false".equals(trimmedText)) {
|
||||
return false;
|
||||
} else {
|
||||
return trimmedText;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+7
@@ -83,6 +83,13 @@ public class VoiceCloneController {
|
||||
return new Result<String>().error(ErrorCode.VOICE_CLONE_NOT_AUDIO_FILE);
|
||||
}
|
||||
|
||||
// 加强验证文件扩展名
|
||||
String originalFilename = voiceFile.getOriginalFilename();
|
||||
String extension = originalFilename.substring(originalFilename.lastIndexOf(".")).toLowerCase();
|
||||
if (!extension.equals(".mp3") && !extension.equals(".wav")) {
|
||||
return new Result<String>().error("只允许上传.mp3和.wav格式的文件");
|
||||
}
|
||||
|
||||
// 验证文件大小 (最大10MB)
|
||||
if (voiceFile.getSize() > 10 * 1024 * 1024) {
|
||||
return new Result<String>().error(ErrorCode.VOICE_CLONE_AUDIO_TOO_LARGE);
|
||||
|
||||
@@ -198,4 +198,7 @@
|
||||
10189=\u9002\u914D\u5668\u64CD\u4F5C\u5931\u8D25
|
||||
10190=\u9002\u914D\u5668\u672A\u627E\u5230
|
||||
10191=\u9002\u914D\u5668\u7F13\u5B58\u9519\u8BEF
|
||||
10192=\u9002\u914D\u5668\u7C7B\u578B\u672A\u627E\u5230
|
||||
10192=\u9002\u914D\u5668\u7C7B\u578B\u672A\u627E\u5230
|
||||
10193=\u8BBE\u5907ID\u4E0D\u80FD\u4E3A\u7A7A
|
||||
10194=\u8BBE\u5907\u4E0D\u5B58\u5728\u6216\u4E0D\u5728\u7EBF
|
||||
10195=OTA\u4E0A\u4F20\u6B21\u6570\u8D85\u8FC7\u9650\u5236
|
||||
@@ -8,23 +8,23 @@
|
||||
10004=Die Kontonummer oder das Passwort ist falsch
|
||||
10005=Konto wurde deaktiviert
|
||||
10006=Eindeutige ID darf nicht leer sein
|
||||
10007=Der Bestätigungscode ist falsch
|
||||
10008=Zuerst Untermenü oder Schaltfläche löschen
|
||||
10009=Das ursprüngliche Passwort ist falsch
|
||||
10010=Das Konto oder Passwort ist falsch, Sie können es noch {0} Mal versuchen
|
||||
10007=Der Best\u00E4tigungscode ist falsch
|
||||
10008=Zuerst Untermen\u00FC oder Schaltfl\u00E4che l\u00F6schen
|
||||
10009=Das urspr\u00FCngliche Passwort ist falsch
|
||||
10010=Das Konto oder Passwort ist falsch, Sie k\u00F6nnen es noch {0} Mal versuchen
|
||||
|
||||
10011=Die übergeordnete Abteilung hat eine falsche Wahl getroffen
|
||||
10012=Oberes Menü kann nicht sich selbst sein
|
||||
10011=Die \u00FCbergeordnete Abteilung hat eine falsche Wahl getroffen
|
||||
10012=Oberes Men\u00FC kann nicht sich selbst sein
|
||||
10013=Datenberechtigungsschnittstelle, die nur ein Map-Typ-Parameter sein kann
|
||||
10014=Bitte löschen Sie zuerst die untergeordnete Abteilung
|
||||
10015=Bitte löschen Sie zuerst den Benutzer unter der Abteilung
|
||||
10014=Bitte l\u00F6schen Sie zuerst die untergeordnete Abteilung
|
||||
10015=Bitte l\u00F6schen Sie zuerst den Benutzer unter der Abteilung
|
||||
10016=Bereitstellung fehlgeschlagen, kein Prozess
|
||||
10017=Das ModellDiagramm ist falsch, bitte überprüfen
|
||||
10017=Das ModellDiagramm ist falsch, bitte \u00FCberpr\u00FCfen
|
||||
10018=Export fehlgeschlagen, Modell-ID ist {0}
|
||||
|
||||
10019=Bitte laden Sie eine Datei hoch
|
||||
10020=Token darf nicht leer sein
|
||||
10021=Token ist ungültig, bitte melden Sie sich erneut an
|
||||
10021=Token ist ung\u00FCltig, bitte melden Sie sich erneut an
|
||||
10022=Das Konto wurde gesperrt
|
||||
10023=Bitte laden Sie Dateien im Format zip, bar, bpmn, bpmn20.xml hoch
|
||||
|
||||
@@ -37,25 +37,25 @@
|
||||
10029=Darf keine illegalen Zeichen enthalten
|
||||
|
||||
10030=Das Passwort hat weniger als {0} Ziffern
|
||||
10031=Das Passwort muss gleichzeitig aus Zahlen, Groß- und Kleinbuchstaben sowie Sonderzeichen bestehen
|
||||
10032=Ausnahme beim Löschen dieser Daten
|
||||
10033=Gerätebestätigungscode Fehler
|
||||
10031=Das Passwort muss gleichzeitig aus Zahlen, Gro\u00DF- und Kleinbuchstaben sowie Sonderzeichen bestehen
|
||||
10032=Ausnahme beim L\u00F6schen dieser Daten
|
||||
10033=Ger\u00E4tebest\u00E4tigungscode Fehler
|
||||
|
||||
10034=Parameterwert darf nicht leer sein
|
||||
10035=Parametertyp darf nicht leer sein
|
||||
10036=Nicht unterstützter Parametertyp
|
||||
10037=Parameterwert muss eine gültige Zahl sein
|
||||
10036=Nicht unterst\u00FCtzter Parametertyp
|
||||
10037=Parameterwert muss eine g\u00FCltige Zahl sein
|
||||
10038=Parameterwert muss true oder false sein
|
||||
10039=Parameterwert muss ein gültiges JSON-Array-Format sein
|
||||
10040=Parameterwert muss ein gültiges JSON-Format sein
|
||||
10039=Parameterwert muss ein g\u00FCltiges JSON-Array-Format sein
|
||||
10040=Parameterwert muss ein g\u00FCltiges JSON-Format sein
|
||||
|
||||
10041=Gerät nicht gefunden
|
||||
10041=Ger\u00E4t nicht gefunden
|
||||
10042={0}
|
||||
10043=Löschen der Daten fehlgeschlagen
|
||||
10043=L\u00F6schen der Daten fehlgeschlagen
|
||||
10044=Benutzer nicht angemeldet
|
||||
10045=WebSocket-Verbindung fehlgeschlagen oder Zeitüberschreitung
|
||||
10045=WebSocket-Verbindung fehlgeschlagen oder Zeit\u00FCberschreitung
|
||||
10046=Stimmabdruck speichern fehlgeschlagen, bitte Administrator kontaktieren
|
||||
10047=Tägliches SendeLimit erreicht
|
||||
10047=T\u00E4gliches SendeLimit erreicht
|
||||
10048=Altes Passwort ist falsch
|
||||
10049=Das eingestellte LLM ist nicht openai oder ollama
|
||||
10050=Token-Generierung fehlgeschlagen
|
||||
@@ -65,45 +65,45 @@
|
||||
10054=Stimmabdruck-Schnittstelle nicht konfiguriert, bitte konfigurieren Sie zuerst die Stimmabdruck-Schnittstellenadresse (server.voice_print) in der Parameterkonfiguration
|
||||
10055=SMS-Sendung fehlgeschlagen
|
||||
10056=SMS-Verbindung konnte nicht hergestellt werden
|
||||
10057=Stimmabdruck für Agent konnte nicht erstellt werden
|
||||
10058=Stimmabdruck für Agent konnte nicht aktualisiert werden
|
||||
10059=Stimmabdruck für Agent konnte nicht gelöscht werden
|
||||
10060=Sendung zu häufig, bitte versuchen Sie es nach {0} Sekunden erneut
|
||||
10057=Stimmabdruck f\u00FCr Agent konnte nicht erstellt werden
|
||||
10058=Stimmabdruck f\u00FCr Agent konnte nicht aktualisiert werden
|
||||
10059=Stimmabdruck f\u00FCr Agent konnte nicht gel\u00F6scht werden
|
||||
10060=Sendung zu h\u00E4ufig, bitte versuchen Sie es nach {0} Sekunden erneut
|
||||
10061=Aktivierungscode darf nicht leer sein
|
||||
10062=Aktivierungscode Fehler
|
||||
10063=Gerät bereits aktiviert
|
||||
10063=Ger\u00E4t bereits aktiviert
|
||||
10064=Dieses Modell ist das Standardmodell, bitte setzen Sie zuerst ein anderes Modell als Standard
|
||||
10065=Hinzufügen der Daten fehlgeschlagen
|
||||
10065=Hinzuf\u00FCgen der Daten fehlgeschlagen
|
||||
10066=Aktualisieren der Daten fehlgeschlagen
|
||||
10067=Grafischer Bestätigungscode Fehler
|
||||
10068=Mobilregistrierung nicht aktiviert, SMS-Bestätigungscode-Funktion kann nicht verwendet werden
|
||||
10067=Grafischer Best\u00E4tigungscode Fehler
|
||||
10068=Mobilregistrierung nicht aktiviert, SMS-Best\u00E4tigungscode-Funktion kann nicht verwendet werden
|
||||
10069=Benutzername ist keine Handynummer, bitte erneut eingeben
|
||||
10070=Diese Handynummer wurde bereits registriert
|
||||
10071=Die eingegebene Handynummer ist nicht registriert
|
||||
10072=Normale Benutzerregistrierung derzeit nicht erlaubt
|
||||
10073=Mobilregistrierung nicht aktiviert, Passwort-Wiederherstellungsfunktion kann nicht verwendet werden
|
||||
10074=Das Format der eingegebenen Handynummer ist falsch
|
||||
10075=Der eingegebene SMS-Bestätigungscode ist falsch
|
||||
10076=Wörterbuchtyp existiert nicht
|
||||
10077=Wörterbuchtypcode ist dupliziert
|
||||
10075=Der eingegebene SMS-Best\u00E4tigungscode ist falsch
|
||||
10076=W\u00F6rterbuchtyp existiert nicht
|
||||
10077=W\u00F6rterbuchtypcode ist dupliziert
|
||||
10078=Lesen der Ressource fehlgeschlagen
|
||||
10079=LLM-Modell und Absichtserkennung, Parameterauswahl stimmt nicht überein
|
||||
10080=Dieser Stimmabdruck gehört zu {0}, der bereits registriert ist, bitte wählen Sie eine andere Stimme
|
||||
10081=Fehler beim Löschen des Stimmabdrucks aufgetreten
|
||||
10082=Änderung nicht erlaubt, diese Stimme wurde bereits als Stimmabdruck registriert ({0})
|
||||
10083=Fehler beim Ändern des Stimmabdrucks, bitte Administrator kontaktieren
|
||||
10084=Stimmabdruck-Schnittstellenadresse Fehler, bitte gehen Sie zur Parameterverwaltung, um die Stimmabdruck-Schnittstellenadresse zu ändern
|
||||
10085=Audiodaten gehören nicht zu diesem Agent
|
||||
10086=Audiodaten sind leer, bitte überprüfen Sie hochgeladene Daten
|
||||
10079=LLM-Modell und Absichtserkennung, Parameterauswahl stimmt nicht \u00FCberein
|
||||
10080=Dieser Stimmabdruck geh\u00F6rt zu {0}, der bereits registriert ist, bitte w\u00E4hlen Sie eine andere Stimme
|
||||
10081=Fehler beim L\u00F6schen des Stimmabdrucks aufgetreten
|
||||
10082=\u00C4nderung nicht erlaubt, diese Stimme wurde bereits als Stimmabdruck registriert ({0})
|
||||
10083=Fehler beim \u00C4ndern des Stimmabdrucks, bitte Administrator kontaktieren
|
||||
10084=Stimmabdruck-Schnittstellenadresse Fehler, bitte gehen Sie zur Parameterverwaltung, um die Stimmabdruck-Schnittstellenadresse zu \u00E4ndern
|
||||
10085=Audiodaten geh\u00F6ren nicht zu diesem Agent
|
||||
10086=Audiodaten sind leer, bitte \u00FCberpr\u00FCfen Sie hochgeladene Daten
|
||||
10087=Stimmabdruck-Registrierung fehlgeschlagen, Anfrage nicht erfolgreich
|
||||
10088=Stimmabdruck-Registrierung fehlgeschlagen, Anfrageverarbeitung fehlgeschlagen
|
||||
10089=Stimmabdruck-Stornierung fehlgeschlagen, Anfrage nicht erfolgreich
|
||||
10090=Stimmabdruck-Stornierung fehlgeschlagen, Anfrageverarbeitung fehlgeschlagen
|
||||
10091=Modellanbieter existiert nicht
|
||||
10092=Das konfigurierte LLM existiert nicht
|
||||
10093=Diese Modellkonfiguration wird von Agent {0} referenziert und kann nicht gelöscht werden
|
||||
10094=Dieses LLM-Modell wird von der Absichtserkennungskonfiguration referenziert und kann nicht gelöscht werden
|
||||
10095=Ungültige Serveroperation
|
||||
10093=Diese Modellkonfiguration wird von Agent {0} referenziert und kann nicht gel\u00F6scht werden
|
||||
10094=Dieses LLM-Modell wird von der Absichtserkennungskonfiguration referenziert und kann nicht gel\u00F6scht werden
|
||||
10095=Ung\u00FCltige Serveroperation
|
||||
10096=Server WebSocket-Adresse nicht konfiguriert
|
||||
10097=Ziel-WebSocket-Adresse existiert nicht
|
||||
10098=WebSocket-Adressliste darf nicht leer sein
|
||||
@@ -115,47 +115,47 @@
|
||||
10104=OTA-Adresse muss mit http oder https beginnen
|
||||
10105=OTA-Adresse muss mit /ota/ enden
|
||||
10106=OTA-Schnittstellenzugriff fehlgeschlagen
|
||||
10107=OTA-Schnittstelle Rückgabeinhaltformat ist falsch
|
||||
10107=OTA-Schnittstelle R\u00FCckgabeinhaltformat ist falsch
|
||||
10108=OTA-Schnittstellenvalidierung fehlgeschlagen
|
||||
10109=MCP-Adresse darf nicht leer sein
|
||||
10110=MCP-Adresse darf nicht localhost oder 127.0.0.1 verwenden
|
||||
10111=Keine gültige MCP-Adresse
|
||||
10111=Keine g\u00FCltige MCP-Adresse
|
||||
10112=MCP-Schnittstellenzugriff fehlgeschlagen
|
||||
10113=MCP-Schnittstelle Rückgabeinhaltformat ist falsch
|
||||
10113=MCP-Schnittstelle R\u00FCckgabeinhaltformat ist falsch
|
||||
10114=MCP-Schnittstellenvalidierung fehlgeschlagen
|
||||
10115=Stimmabdruck-Schnittstellenadresse darf nicht leer sein
|
||||
10116=Stimmabdruck-Schnittstellenadresse darf nicht localhost oder 127.0.0.1 verwenden
|
||||
10117=Keine gültige Stimmabdruck-Schnittstellenadresse
|
||||
10117=Keine g\u00FCltige Stimmabdruck-Schnittstellenadresse
|
||||
10118=Stimmabdruck-Schnittstellenadresse muss mit http oder https beginnen
|
||||
10119=Stimmabdruck-Schnittstellenzugriff fehlgeschlagen
|
||||
10120=Stimmabdruck-Schnittstelle Rückgabeinhaltformat ist falsch
|
||||
10120=Stimmabdruck-Schnittstelle R\u00FCckgabeinhaltformat ist falsch
|
||||
10121=Stimmabdruck-Schnittstellenvalidierung fehlgeschlagen
|
||||
10122=MQTT-Geheimnis darf nicht leer sein
|
||||
10123=Ihr MQTT-Geheimnis ist nicht sicher, es muss mindestens 8 Zeichen lang sein und muss sowohl Groß- als auch Kleinbuchstaben enthalten
|
||||
10124=Ihr MQTT-Geheimnis ist nicht sicher, MQTT-Geheimnis muss sowohl Groß- als auch Kleinbuchstaben enthalten
|
||||
10125=Ihr MQTT-Geheimnis enthält schwaches Passwort
|
||||
10128=Wörterbuchlabel ist dupliziert
|
||||
10129=SM2-Schlüssel nicht konfiguriert
|
||||
10130=SM2-Entschlüsselung fehlgeschlagen
|
||||
10131=modelType und provideCode dürfen nicht leer sein
|
||||
10123=Ihr MQTT-Geheimnis ist nicht sicher, es muss mindestens 8 Zeichen lang sein und muss sowohl Gro\u00DF- als auch Kleinbuchstaben enthalten
|
||||
10124=Ihr MQTT-Geheimnis ist nicht sicher, MQTT-Geheimnis muss sowohl Gro\u00DF- als auch Kleinbuchstaben enthalten
|
||||
10125=Ihr MQTT-Geheimnis enth\u00E4lt schwaches Passwort
|
||||
10128=W\u00F6rterbuchlabel ist dupliziert
|
||||
10129=SM2-Schl\u00FCssel nicht konfiguriert
|
||||
10130=SM2-Entschl\u00FCsselung fehlgeschlagen
|
||||
10131=modelType und provideCode d\u00FCrfen nicht leer sein
|
||||
10132=Keine Berechtigung, den Chat-Verlauf dieses Agents anzuzeigen
|
||||
10133=Sitzungs-ID darf nicht leer sein
|
||||
10134=Agent-ID darf nicht leer sein
|
||||
10135=Chat-Verlauf-Download fehlgeschlagen
|
||||
10136=Download-Link abgelaufen oder ungültig
|
||||
10137=Download-Link ungültig
|
||||
10136=Download-Link abgelaufen oder ung\u00FCltig
|
||||
10137=Download-Link ung\u00FCltig
|
||||
10138=Benutzer
|
||||
10139=Agent
|
||||
10140=Audio-Datei darf nicht leer sein
|
||||
10141=Nur Audio-Dateien werden unterstützt
|
||||
10142=Audio-Dateigröße darf 10MB nicht überschreiten
|
||||
10141=Nur Audio-Dateien werden unterst\u00FCtzt
|
||||
10142=Audio-Dateigr\u00F6\u00DFe darf 10MB nicht \u00FCberschreiten
|
||||
10143=Upload fehlgeschlagen
|
||||
10144=Stimmenklon-Datensatz existiert nicht
|
||||
10145=Stimmressourceninformationen dürfen nicht leer sein
|
||||
10145=Stimmressourceninformationen d\u00FCrfen nicht leer sein
|
||||
10146=TTS-Plattformname darf nicht leer sein
|
||||
10147=Stimmen-ID darf nicht leer sein
|
||||
10148=Kontoinhaber darf nicht leer sein
|
||||
10149=Zu löschende Stimmressourcen-ID darf nicht leer sein
|
||||
10149=Zu l\u00F6schende Stimmressourcen-ID darf nicht leer sein
|
||||
10150=Sie haben keine Berechtigung, diesen Datensatz zu bearbeiten
|
||||
10151=Bitte laden Sie zuerst eine Audio-Datei hoch
|
||||
10152=Modellkonfiguration nicht gefunden
|
||||
@@ -179,18 +179,18 @@
|
||||
10170=Wissensbasis-Name existiert bereits
|
||||
10171=RAG-Konfiguration base_url darf nicht leer sein
|
||||
10172=RAG-Konfiguration api_key darf nicht leer sein
|
||||
10173=RAG-Konfiguration api_key darf keinen Platzhalter enthalten, bitte ersetzen Sie mit tatsächlichem API-Schlüssel
|
||||
10173=RAG-Konfiguration api_key darf keinen Platzhalter enthalten, bitte ersetzen Sie mit tats\u00E4chlichem API-Schl\u00FCssel
|
||||
10174=RAG-Konfiguration base_url-Formatfehler, muss mit http oder https beginnen
|
||||
10175=Mac-Adresse darf nicht leer sein
|
||||
10176=RAG-Konfiguration dataset_id darf nicht leer sein
|
||||
10177=RAG-Konfiguration model_id darf nicht leer sein
|
||||
10178=RAG-Konfiguration dataset_id und model_id dürfen nicht leer sein
|
||||
10178=RAG-Konfiguration dataset_id und model_id d\u00FCrfen nicht leer sein
|
||||
10179=Dateiname darf nicht leer sein
|
||||
10180=Dateiinhalt darf nicht leer sein
|
||||
10181=Stimmenklon-Name darf nicht leer sein
|
||||
10182=Stimmenklon-Audio nicht gefunden
|
||||
10183=Standard-Agent-Vorlage nicht gefunden
|
||||
10184=Nicht unterstützter Adaptertyp
|
||||
10184=Nicht unterst\u00FCtzter Adaptertyp
|
||||
10185=RAG-Konfigurationsvalidierung fehlgeschlagen
|
||||
10186=Adapter-Erstellung fehlgeschlagen
|
||||
10187=Adapter-Initialisierung fehlgeschlagen
|
||||
@@ -198,4 +198,7 @@
|
||||
10189=Adapter-Operation fehlgeschlagen
|
||||
10190=Adapter nicht gefunden
|
||||
10191=Adapter-Cache-Fehler
|
||||
10192=Adaptertyp nicht gefunden
|
||||
10192=Adaptertyp nicht gefunden
|
||||
10193=Adapter-ID darf nicht leer sein
|
||||
10194=Adapter nicht gefunden oder nicht online
|
||||
10195=OTA-Upload-Anzahl \u00FCberschreitet das Limit
|
||||
@@ -198,4 +198,7 @@
|
||||
10189=Adapter operation failed
|
||||
10190=Adapter not found
|
||||
10191=Adapter cache error
|
||||
10192=Adapter type not found
|
||||
10192=Adapter type not found
|
||||
10193=Device ID cannot be empty
|
||||
10194=Device not found or not online
|
||||
10195=OTA upload times exceed the limit
|
||||
@@ -1,201 +1,204 @@
|
||||
#Tiếng Việt
|
||||
500=Ngoại lệ nội bộ máy chủ
|
||||
401=Không được ủy quyền
|
||||
403=Truy cập bị từ chối, không có quyền
|
||||
10001={0} không thể để trống
|
||||
10002=Bản ghi đã tồn tại trong cơ sở dữ liệu
|
||||
10003=Không thể lấy tham số
|
||||
10004=Số tài khoản hoặc mật khẩu không chính xác
|
||||
10005=Tài khoản đã bị vô hiệu hóa
|
||||
10006=ID duy nhất không thể để trống
|
||||
10007=Mã xác minh không chính xác
|
||||
10008=Trước tiên hãy xóa menu con hoặc nút
|
||||
10009=Mật khẩu gốc không chính xác
|
||||
10010=Tài khoản hoặc mật khẩu không chính xác, bạn có thể thử thêm {0} lần nữa
|
||||
#Ti\u1EBFng Vi\u1EC7t
|
||||
500=Ngo\u1EA1i l\u1EC7 n\u1ED9i b\u1ED9 m\u00E1y ch\u1EE7
|
||||
401=Kh\u00F4ng \u0111\u01B0\u1EE3c \u1EE7y quy\u1EC1n
|
||||
403=Truy c\u1EADp b\u1ECB t\u1EEB ch\u1ED1i, kh\u00F4ng c\u00F3 quy\u1EC1n
|
||||
10001={0} kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10002=B\u1EA3n ghi \u0111\u00E3 t\u1ED3n t\u1EA1i trong c\u01A1 s\u1EDF d\u1EEF li\u1EC7u
|
||||
10003=Kh\u00F4ng th\u1EC3 l\u1EA5y tham s\u1ED1
|
||||
10004=S\u1ED1 t\u00E0i kho\u1EA3n ho\u1EB7c m\u1EADt kh\u1EA9u kh\u00F4ng ch\u00EDnh x\u00E1c
|
||||
10005=T\u00E0i kho\u1EA3n \u0111\u00E3 b\u1ECB v\u00F4 hi\u1EC7u h\u00F3a
|
||||
10006=ID duy nh\u1EA5t kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10007=M\u00E3 x\u00E1c minh kh\u00F4ng ch\u00EDnh x\u00E1c
|
||||
10008=Tr\u01B0\u1EDBc ti\u00EAn h\u00E3y x\u00F3a menu con ho\u1EB7c n\u00FAt
|
||||
10009=M\u1EADt kh\u1EA9u g\u1ED1c kh\u00F4ng ch\u00EDnh x\u00E1c
|
||||
10010=T\u00E0i kho\u1EA3n ho\u1EB7c m\u1EADt kh\u1EA9u kh\u00F4ng ch\u00EDnh x\u00E1c, b\u1EA1n c\u00F3 th\u1EC3 th\u1EED th\u00EAm {0} l\u1EA7n n\u1EEFa
|
||||
|
||||
10011=Phòng ban cấp trên đã lựa chọn sai
|
||||
10012=Menu trên không thể là chính nó
|
||||
10013=Giao diện quyền dữ liệu, chỉ có thể là tham số kiểu Map
|
||||
10014=Vui lòng xóa phòng ban cấp dưới trước
|
||||
10015=Vui lòng xóa người dùng thuộc phòng ban trước
|
||||
10016=Triển khai thất bại, không có quy trình
|
||||
10017=Biểu đồ mô hình không chính xác, vui lòng kiểm tra
|
||||
10018=Xuất thất bại, ID mô hình là {0}
|
||||
10011=Ph\u00F2ng ban c\u1EA5p tr\u00EAn \u0111\u00E3 l\u1EF1a ch\u1ECDn sai
|
||||
10012=Menu tr\u00EAn kh\u00F4ng th\u1EC3 l\u00E0 ch\u00EDnh n\u00F3
|
||||
10013=Giao di\u1EC7n quy\u1EC1n d\u1EEF li\u1EC7u, ch\u1EC9 c\u00F3 th\u1EC3 l\u00E0 tham s\u1ED1 ki\u1EC3u Map
|
||||
10014=Vui l\u00F2ng x\u00F3a ph\u00F2ng ban c\u1EA5p d\u01B0\u1EDBi tr\u01B0\u1EDBc
|
||||
10015=Vui l\u00F2ng x\u00F3a ng\u01B0\u1EDDi d\u00F9ng thu\u1ED9c ph\u00F2ng ban tr\u01B0\u1EDBc
|
||||
10016=Tri\u1EC3n khai th\u1EA5t b\u1EA1i, kh\u00F4ng c\u00F3 quy tr\u00ECnh
|
||||
10017=Bi\u1EC3u \u0111\u1ED3 m\u00F4 h\u00ECnh kh\u00F4ng ch\u00EDnh x\u00E1c, vui l\u00F2ng ki\u1EC3m tra
|
||||
10018=Xu\u1EA5t th\u1EA5t b\u1EA1i, ID m\u00F4 h\u00ECnh l\u00E0 {0}
|
||||
|
||||
10019=Vui lòng tải lên tệp
|
||||
10020=Token không thể để trống
|
||||
10021=Token không hợp lệ, vui lòng đăng nhập lại
|
||||
10022=Tài khoản đã bị khóa
|
||||
10023=Vui lòng tải lên tệp ở định dạng zip, bar, bpmn, bpmn20.xml
|
||||
10019=Vui l\u00F2ng t\u1EA3i l\u00EAn t\u1EC7p
|
||||
10020=Token kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10021=Token kh\u00F4ng h\u1EE3p l\u1EC7, vui l\u00F2ng \u0111\u0103ng nh\u1EADp l\u1EA1i
|
||||
10022=T\u00E0i kho\u1EA3n \u0111\u00E3 b\u1ECB kh\u00F3a
|
||||
10023=Vui l\u00F2ng t\u1EA3i l\u00EAn t\u1EC7p \u1EDF \u0111\u1ECBnh d\u1EA1ng zip, bar, bpmn, bpmn20.xml
|
||||
|
||||
10024=Tải lên tệp {0} thất bại
|
||||
10025=Gửi SMS {0} thất bại
|
||||
10026=Mẫu email không tồn tại
|
||||
10024=T\u1EA3i l\u00EAn t\u1EC7p {0} th\u1EA5t b\u1EA1i
|
||||
10025=G\u1EEDi SMS {0} th\u1EA5t b\u1EA1i
|
||||
10026=M\u1EABu email kh\u00F4ng t\u1ED3n t\u1EA1i
|
||||
|
||||
10027=Ngoại lệ dịch vụ Redis
|
||||
10028=Tác vụ hẹn giờ thất bại
|
||||
10029=Không thể chứa ký tự không hợp lệ
|
||||
10027=Ngo\u1EA1i l\u1EC7 d\u1ECBch v\u1EE5 Redis
|
||||
10028=T\u00E1c v\u1EE5 h\u1EB9n gi\u1EDD th\u1EA5t b\u1EA1i
|
||||
10029=Kh\u00F4ng th\u1EC3 ch\u1EE9a k\u00FD t\u1EF1 kh\u00F4ng h\u1EE3p l\u1EC7
|
||||
|
||||
10030=Mật khẩu ít hơn {0} chữ số
|
||||
10031=Mật khẩu phải bao gồm số, chữ hoa, chữ thường và ký tự đặc biệt cùng một lúc
|
||||
10032=Ngoại lệ khi xóa dữ liệu này
|
||||
10033=Lỗi mã xác minh thiết bị
|
||||
10030=M\u1EADt kh\u1EA9u \u00EDt h\u01A1n {0} ch\u1EEF s\u1ED1
|
||||
10031=M\u1EADt kh\u1EA9u ph\u1EA3i bao g\u1ED3m s\u1ED1, ch\u1EEF hoa, ch\u1EEF th\u01B0\u1EDDng v\u00E0 k\u00FD t\u1EF1 \u0111\u1EB7c bi\u1EC7t c\u00F9ng m\u1ED9t l\u00FAc
|
||||
10032=Ngo\u1EA1i l\u1EC7 khi x\u00F3a d\u1EEF li\u1EC7u n\u00E0y
|
||||
10033=L\u1ED7i m\u00E3 x\u00E1c minh thi\u1EBFt b\u1ECB
|
||||
|
||||
10034=Giá trị tham số không thể để trống
|
||||
10035=Loại tham số không thể để trống
|
||||
10036=Loại tham số không được hỗ trợ
|
||||
10037=Giá trị tham số phải là số hợp lệ
|
||||
10038=Giá trị tham số phải là true hoặc false
|
||||
10039=Giá trị tham số phải là định dạng mảng JSON hợp lệ
|
||||
10040=Giá trị tham số phải là định dạng JSON hợp lệ
|
||||
10034=Gi\u00E1 tr\u1ECB tham s\u1ED1 kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10035=Lo\u1EA1i tham s\u1ED1 kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10036=Lo\u1EA1i tham s\u1ED1 kh\u00F4ng \u0111\u01B0\u1EE3c h\u1ED7 tr\u1EE3
|
||||
10037=Gi\u00E1 tr\u1ECB tham s\u1ED1 ph\u1EA3i l\u00E0 s\u1ED1 h\u1EE3p l\u1EC7
|
||||
10038=Gi\u00E1 tr\u1ECB tham s\u1ED1 ph\u1EA3i l\u00E0 true ho\u1EB7c false
|
||||
10039=Gi\u00E1 tr\u1ECB tham s\u1ED1 ph\u1EA3i l\u00E0 \u0111\u1ECBnh d\u1EA1ng m\u1EA3ng JSON h\u1EE3p l\u1EC7
|
||||
10040=Gi\u00E1 tr\u1ECB tham s\u1ED1 ph\u1EA3i l\u00E0 \u0111\u1ECBnh d\u1EA1ng JSON h\u1EE3p l\u1EC7
|
||||
|
||||
10041=Không tìm thấy thiết bị
|
||||
10041=Kh\u00F4ng t\u00ECm th\u1EA5y thi\u1EBFt b\u1ECB
|
||||
10042={0}
|
||||
10043=Xóa dữ liệu thất bại
|
||||
10044=Người dùng chưa đăng nhập
|
||||
10045=Kết nối WebSocket thất bại hoặc hết thời gian chờ
|
||||
10046=Không thể lưu dấu giọng nói, vui lòng liên hệ quản trị viên
|
||||
10047=Đạt đến giới hạn gửi hàng ngày
|
||||
10048=Mật khẩu cũ không chính xác
|
||||
10049=LLM được đặt không phải là openai hoặc ollama
|
||||
10050=Không thể tạo token
|
||||
10051=Không tìm thấy tài nguyên
|
||||
10052=Không tìm thấy agent mặc định
|
||||
10053=Không tìm thấy agent
|
||||
10054=Giao diện dấu giọng nói chưa được cấu hình, vui lòng cấu hình địa chỉ giao diện dấu giọng nói (server.voice_print) trong cấu hình tham số trước
|
||||
10055=Gửi SMS thất bại
|
||||
10056=Không thể thiết lập kết nối SMS
|
||||
10057=Không thể tạo dấu giọng nói cho agent
|
||||
10058=Không thể cập nhật dấu giọng nói cho agent
|
||||
10059=Không thể xóa dấu giọng nói cho agent
|
||||
10060=Gửi quá thường xuyên, vui lòng thử lại sau {0} giây
|
||||
10061=Mã kích hoạt không thể để trống
|
||||
10062=Lỗi mã kích hoạt
|
||||
10063=Thiết bị đã được kích hoạt
|
||||
10064=Mô hình này là mô hình mặc định, vui lòng đặt mô hình khác làm mặc định trước
|
||||
10065=Thêm dữ liệu thất bại
|
||||
10066=Cập nhật dữ liệu thất bại
|
||||
10067=Lỗi mã xác minh đồ họa
|
||||
10068=Đăng ký di động không được bật, không thể sử dụng chức năng mã xác minh SMS
|
||||
10069=Tên người dùng không phải là số điện thoại di động, vui lòng nhập lại
|
||||
10070=Số điện thoại di động này đã được đăng ký
|
||||
10071=Số điện thoại di động đã nhập không được đăng ký
|
||||
10072=Hiện không cho phép đăng ký người dùng thông thường
|
||||
10073=Đăng ký di động không được bật, không thể sử dụng chức năng truy xuất mật khẩu
|
||||
10074=Định dạng số điện thoại di động đã nhập không chính xác
|
||||
10075=Mã xác minh SMS đã nhập không chính xác
|
||||
10076=Loại từ điển không tồn tại
|
||||
10077=Mã loại từ điển bị trùng lặp
|
||||
10078=Đọc tài nguyên thất bại
|
||||
10079=Mô hình LLM và nhận dạng ý định, lựa chọn tham số không khớp
|
||||
10080=Dấu giọng nói này thuộc về {0} đã đăng ký, vui lòng chọn giọng nói khác
|
||||
10081=Đã xảy ra lỗi khi xóa dấu giọng nói
|
||||
10082=Không cho phép sửa đổi, giọng nói này đã được đăng ký làm dấu giọng nói ({0})
|
||||
10083=Lỗi sửa đổi dấu giọng nói, vui lòng liên hệ quản trị viên
|
||||
10084=Lỗi địa chỉ giao diện dấu giọng nói, vui lòng vào quản lý tham số để sửa đổi địa chỉ giao diện dấu giọng nói
|
||||
10085=Dữ liệu âm thanh không thuộc về agent này
|
||||
10086=Dữ liệu âm thanh trống, vui lòng kiểm tra dữ liệu đã tải lên
|
||||
10087=Đăng ký dấu giọng nói thất bại, yêu cầu không thành công
|
||||
10088=Đăng ký dấu giọng nói thất bại, xử lý yêu cầu thất bại
|
||||
10089=Hủy dấu giọng nói thất bại, yêu cầu không thành công
|
||||
10090=Hủy dấu giọng nói thất bại, xử lý yêu cầu thất bại
|
||||
10091=Nhà cung cấp mô hình không tồn tại
|
||||
10092=LLM được cấu hình không tồn tại
|
||||
10093=Cấu hình mô hình này được tham chiếu bởi agent {0} và không thể xóa
|
||||
10094=Mô hình LLM này được tham chiếu bởi cấu hình nhận dạng ý định và không thể xóa
|
||||
10095=Thao tác máy chủ không hợp lệ
|
||||
10096=Địa chỉ WebSocket máy chủ chưa được cấu hình
|
||||
10097=Địa chỉ WebSocket mục tiêu không tồn tại
|
||||
10098=Danh sách địa chỉ WebSocket không thể để trống
|
||||
10099=Địa chỉ WebSocket không thể sử dụng localhost hoặc 127.0.0.1
|
||||
10100=Định dạng địa chỉ WebSocket không chính xác
|
||||
10101=Kiểm tra kết nối WebSocket thất bại
|
||||
10102=Địa chỉ OTA không thể để trống
|
||||
10103=Địa chỉ OTA không thể sử dụng localhost hoặc 127.0.0.1
|
||||
10104=Địa chỉ OTA phải bắt đầu bằng http hoặc https
|
||||
10105=Địa chỉ OTA phải kết thúc bằng /ota/
|
||||
10106=Truy cập giao diện OTA thất bại
|
||||
10107=Định dạng nội dung trả về giao diện OTA không chính xác
|
||||
10108=Xác thực giao diện OTA thất bại
|
||||
10109=Địa chỉ MCP không thể để trống
|
||||
10110=Địa chỉ MCP không thể sử dụng localhost hoặc 127.0.0.1
|
||||
10111=Không phải địa chỉ MCP hợp lệ
|
||||
10112=Truy cập giao diện MCP thất bại
|
||||
10113=Định dạng nội dung trả về giao diện MCP không chính xác
|
||||
10114=Xác thực giao diện MCP thất bại
|
||||
10115=Địa chỉ giao diện dấu giọng nói không thể để trống
|
||||
10116=Địa chỉ giao diện dấu giọng nói không thể sử dụng localhost hoặc 127.0.0.1
|
||||
10117=Không phải địa chỉ giao diện dấu giọng nói hợp lệ
|
||||
10118=Địa chỉ giao diện dấu giọng nói phải bắt đầu bằng http hoặc https
|
||||
10119=Truy cập giao diện dấu giọng nói thất bại
|
||||
10120=Định dạng nội dung trả về giao diện dấu giọng nói không chính xác
|
||||
10121=Xác thực giao diện dấu giọng nói thất bại
|
||||
10122=Bí mật MQTT không thể để trống
|
||||
10123=Bí mật MQTT của bạn không an toàn, cần ít nhất 8 ký tự và phải chứa cả chữ hoa và chữ thường
|
||||
10124=Bí mật MQTT của bạn không an toàn, bí mật MQTT phải chứa cả chữ hoa và chữ thường
|
||||
10125=Bí mật MQTT của bạn chứa mật khẩu yếu
|
||||
10128=Nhãn từ điển bị trùng lặp
|
||||
10129=Khóa SM2 chưa được cấu hình
|
||||
10130=Giải mã SM2 thất bại
|
||||
10131=modelType và provideCode không thể để trống
|
||||
10132=Không có quyền xem lịch sử trò chuyện của agent này
|
||||
10133=ID phiên không thể để trống
|
||||
10134=ID agent không thể để trống
|
||||
10135=Tải xuống lịch sử trò chuyện thất bại
|
||||
10136=Liên kết tải xuống đã hết hạn hoặc không hợp lệ
|
||||
10137=Liên kết tải xuống không hợp lệ
|
||||
10138=Người dùng
|
||||
10043=X\u00F3a d\u1EEF li\u1EC7u th\u1EA5t b\u1EA1i
|
||||
10044=Ng\u01B0\u1EDDi d\u00F9ng ch\u01B0a \u0111\u0103ng nh\u1EADp
|
||||
10045=K\u1EBFt n\u1ED1i WebSocket th\u1EA5t b\u1EA1i ho\u1EB7c h\u1EBFt th\u1EDDi gian ch\u1EDD
|
||||
10046=Kh\u00F4ng th\u1EC3 l\u01B0u d\u1EA5u gi\u1ECDng n\u00F3i, vui l\u00F2ng li\u00EAn h\u1EC7 qu\u1EA3n tr\u1ECB vi\u00EAn
|
||||
10047=\u0110\u1EA1t \u0111\u1EBFn gi\u1EDBi h\u1EA1n g\u1EEDi h\u00E0ng ng\u00E0y
|
||||
10048=M\u1EADt kh\u1EA9u c\u0169 kh\u00F4ng ch\u00EDnh x\u00E1c
|
||||
10049=LLM \u0111\u01B0\u1EE3c \u0111\u1EB7t kh\u00F4ng ph\u1EA3i l\u00E0 openai ho\u1EB7c ollama
|
||||
10050=Kh\u00F4ng th\u1EC3 t\u1EA1o token
|
||||
10051=Kh\u00F4ng t\u00ECm th\u1EA5y t\u00E0i nguy\u00EAn
|
||||
10052=Kh\u00F4ng t\u00ECm th\u1EA5y agent m\u1EB7c \u0111\u1ECBnh
|
||||
10053=Kh\u00F4ng t\u00ECm th\u1EA5y agent
|
||||
10054=Giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i ch\u01B0a \u0111\u01B0\u1EE3c c\u1EA5u h\u00ECnh, vui l\u00F2ng c\u1EA5u h\u00ECnh \u0111\u1ECBa ch\u1EC9 giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i (server.voice_print) trong c\u1EA5u h\u00ECnh tham s\u1ED1 tr\u01B0\u1EDBc
|
||||
10055=G\u1EEDi SMS th\u1EA5t b\u1EA1i
|
||||
10056=Kh\u00F4ng th\u1EC3 thi\u1EBFt l\u1EADp k\u1EBFt n\u1ED1i SMS
|
||||
10057=Kh\u00F4ng th\u1EC3 t\u1EA1o d\u1EA5u gi\u1ECDng n\u00F3i cho agent
|
||||
10058=Kh\u00F4ng th\u1EC3 c\u1EADp nh\u1EADt d\u1EA5u gi\u1ECDng n\u00F3i cho agent
|
||||
10059=Kh\u00F4ng th\u1EC3 x\u00F3a d\u1EA5u gi\u1ECDng n\u00F3i cho agent
|
||||
10060=G\u1EEDi qu\u00E1 th\u01B0\u1EDDng xuy\u00EAn, vui l\u00F2ng th\u1EED l\u1EA1i sau {0} gi\u00E2y
|
||||
10061=M\u00E3 k\u00EDch ho\u1EA1t kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10062=L\u1ED7i m\u00E3 k\u00EDch ho\u1EA1t
|
||||
10063=Thi\u1EBFt b\u1ECB \u0111\u00E3 \u0111\u01B0\u1EE3c k\u00EDch ho\u1EA1t
|
||||
10064=M\u00F4 h\u00ECnh n\u00E0y l\u00E0 m\u00F4 h\u00ECnh m\u1EB7c \u0111\u1ECBnh, vui l\u00F2ng \u0111\u1EB7t m\u00F4 h\u00ECnh kh\u00E1c l\u00E0m m\u1EB7c \u0111\u1ECBnh tr\u01B0\u1EDBc
|
||||
10065=Th\u00EAm d\u1EEF li\u1EC7u th\u1EA5t b\u1EA1i
|
||||
10066=C\u1EADp nh\u1EADt d\u1EEF li\u1EC7u th\u1EA5t b\u1EA1i
|
||||
10067=L\u1ED7i m\u00E3 x\u00E1c minh \u0111\u1ED3 h\u1ECDa
|
||||
10068=\u0110\u0103ng k\u00FD di \u0111\u1ED9ng kh\u00F4ng \u0111\u01B0\u1EE3c b\u1EADt, kh\u00F4ng th\u1EC3 s\u1EED d\u1EE5ng ch\u1EE9c n\u0103ng m\u00E3 x\u00E1c minh SMS
|
||||
10069=T\u00EAn ng\u01B0\u1EDDi d\u00F9ng kh\u00F4ng ph\u1EA3i l\u00E0 s\u1ED1 \u0111i\u1EC7n tho\u1EA1i di \u0111\u1ED9ng, vui l\u00F2ng nh\u1EADp l\u1EA1i
|
||||
10070=S\u1ED1 \u0111i\u1EC7n tho\u1EA1i di \u0111\u1ED9ng n\u00E0y \u0111\u00E3 \u0111\u01B0\u1EE3c \u0111\u0103ng k\u00FD
|
||||
10071=S\u1ED1 \u0111i\u1EC7n tho\u1EA1i di \u0111\u1ED9ng \u0111\u00E3 nh\u1EADp kh\u00F4ng \u0111\u01B0\u1EE3c \u0111\u0103ng k\u00FD
|
||||
10072=Hi\u1EC7n kh\u00F4ng cho ph\u00E9p \u0111\u0103ng k\u00FD ng\u01B0\u1EDDi d\u00F9ng th\u00F4ng th\u01B0\u1EDDng
|
||||
10073=\u0110\u0103ng k\u00FD di \u0111\u1ED9ng kh\u00F4ng \u0111\u01B0\u1EE3c b\u1EADt, kh\u00F4ng th\u1EC3 s\u1EED d\u1EE5ng ch\u1EE9c n\u0103ng truy xu\u1EA5t m\u1EADt kh\u1EA9u
|
||||
10074=\u0110\u1ECBnh d\u1EA1ng s\u1ED1 \u0111i\u1EC7n tho\u1EA1i di \u0111\u1ED9ng \u0111\u00E3 nh\u1EADp kh\u00F4ng ch\u00EDnh x\u00E1c
|
||||
10075=M\u00E3 x\u00E1c minh SMS \u0111\u00E3 nh\u1EADp kh\u00F4ng ch\u00EDnh x\u00E1c
|
||||
10076=Lo\u1EA1i t\u1EEB \u0111i\u1EC3n kh\u00F4ng t\u1ED3n t\u1EA1i
|
||||
10077=M\u00E3 lo\u1EA1i t\u1EEB \u0111i\u1EC3n b\u1ECB tr\u00F9ng l\u1EB7p
|
||||
10078=\u0110\u1ECDc t\u00E0i nguy\u00EAn th\u1EA5t b\u1EA1i
|
||||
10079=M\u00F4 h\u00ECnh LLM v\u00E0 nh\u1EADn d\u1EA1ng \u00FD \u0111\u1ECBnh, l\u1EF1a ch\u1ECDn tham s\u1ED1 kh\u00F4ng kh\u1EDBp
|
||||
10080=D\u1EA5u gi\u1ECDng n\u00F3i n\u00E0y thu\u1ED9c v\u1EC1 {0} \u0111\u00E3 \u0111\u0103ng k\u00FD, vui l\u00F2ng ch\u1ECDn gi\u1ECDng n\u00F3i kh\u00E1c
|
||||
10081=\u0110\u00E3 x\u1EA3y ra l\u1ED7i khi x\u00F3a d\u1EA5u gi\u1ECDng n\u00F3i
|
||||
10082=Kh\u00F4ng cho ph\u00E9p s\u1EEDa \u0111\u1ED5i, gi\u1ECDng n\u00F3i n\u00E0y \u0111\u00E3 \u0111\u01B0\u1EE3c \u0111\u0103ng k\u00FD l\u00E0m d\u1EA5u gi\u1ECDng n\u00F3i ({0})
|
||||
10083=L\u1ED7i s\u1EEDa \u0111\u1ED5i d\u1EA5u gi\u1ECDng n\u00F3i, vui l\u00F2ng li\u00EAn h\u1EC7 qu\u1EA3n tr\u1ECB vi\u00EAn
|
||||
10084=L\u1ED7i \u0111\u1ECBa ch\u1EC9 giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i, vui l\u00F2ng v\u00E0o qu\u1EA3n l\u00FD tham s\u1ED1 \u0111\u1EC3 s\u1EEDa \u0111\u1ED5i \u0111\u1ECBa ch\u1EC9 giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i
|
||||
10085=D\u1EEF li\u1EC7u \u00E2m thanh kh\u00F4ng thu\u1ED9c v\u1EC1 agent n\u00E0y
|
||||
10086=D\u1EEF li\u1EC7u \u00E2m thanh tr\u1ED1ng, vui l\u00F2ng ki\u1EC3m tra d\u1EEF li\u1EC7u \u0111\u00E3 t\u1EA3i l\u00EAn
|
||||
10087=\u0110\u0103ng k\u00FD d\u1EA5u gi\u1ECDng n\u00F3i th\u1EA5t b\u1EA1i, y\u00EAu c\u1EA7u kh\u00F4ng th\u00E0nh c\u00F4ng
|
||||
10088=\u0110\u0103ng k\u00FD d\u1EA5u gi\u1ECDng n\u00F3i th\u1EA5t b\u1EA1i, x\u1EED l\u00FD y\u00EAu c\u1EA7u th\u1EA5t b\u1EA1i
|
||||
10089=H\u1EE7y d\u1EA5u gi\u1ECDng n\u00F3i th\u1EA5t b\u1EA1i, y\u00EAu c\u1EA7u kh\u00F4ng th\u00E0nh c\u00F4ng
|
||||
10090=H\u1EE7y d\u1EA5u gi\u1ECDng n\u00F3i th\u1EA5t b\u1EA1i, x\u1EED l\u00FD y\u00EAu c\u1EA7u th\u1EA5t b\u1EA1i
|
||||
10091=Nh\u00E0 cung c\u1EA5p m\u00F4 h\u00ECnh kh\u00F4ng t\u1ED3n t\u1EA1i
|
||||
10092=LLM \u0111\u01B0\u1EE3c c\u1EA5u h\u00ECnh kh\u00F4ng t\u1ED3n t\u1EA1i
|
||||
10093=C\u1EA5u h\u00ECnh m\u00F4 h\u00ECnh n\u00E0y \u0111\u01B0\u1EE3c tham chi\u1EBFu b\u1EDFi agent {0} v\u00E0 kh\u00F4ng th\u1EC3 x\u00F3a
|
||||
10094=M\u00F4 h\u00ECnh LLM n\u00E0y \u0111\u01B0\u1EE3c tham chi\u1EBFu b\u1EDFi c\u1EA5u h\u00ECnh nh\u1EADn d\u1EA1ng \u00FD \u0111\u1ECBnh v\u00E0 kh\u00F4ng th\u1EC3 x\u00F3a
|
||||
10095=Thao t\u00E1c m\u00E1y ch\u1EE7 kh\u00F4ng h\u1EE3p l\u1EC7
|
||||
10096=\u0110\u1ECBa ch\u1EC9 WebSocket m\u00E1y ch\u1EE7 ch\u01B0a \u0111\u01B0\u1EE3c c\u1EA5u h\u00ECnh
|
||||
10097=\u0110\u1ECBa ch\u1EC9 WebSocket m\u1EE5c ti\u00EAu kh\u00F4ng t\u1ED3n t\u1EA1i
|
||||
10098=Danh s\u00E1ch \u0111\u1ECBa ch\u1EC9 WebSocket kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10099=\u0110\u1ECBa ch\u1EC9 WebSocket kh\u00F4ng th\u1EC3 s\u1EED d\u1EE5ng localhost ho\u1EB7c 127.0.0.1
|
||||
10100=\u0110\u1ECBnh d\u1EA1ng \u0111\u1ECBa ch\u1EC9 WebSocket kh\u00F4ng ch\u00EDnh x\u00E1c
|
||||
10101=Ki\u1EC3m tra k\u1EBFt n\u1ED1i WebSocket th\u1EA5t b\u1EA1i
|
||||
10102=\u0110\u1ECBa ch\u1EC9 OTA kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10103=\u0110\u1ECBa ch\u1EC9 OTA kh\u00F4ng th\u1EC3 s\u1EED d\u1EE5ng localhost ho\u1EB7c 127.0.0.1
|
||||
10104=\u0110\u1ECBa ch\u1EC9 OTA ph\u1EA3i b\u1EAFt \u0111\u1EA7u b\u1EB1ng http ho\u1EB7c https
|
||||
10105=\u0110\u1ECBa ch\u1EC9 OTA ph\u1EA3i k\u1EBFt th\u00FAc b\u1EB1ng /ota/
|
||||
10106=Truy c\u1EADp giao di\u1EC7n OTA th\u1EA5t b\u1EA1i
|
||||
10107=\u0110\u1ECBnh d\u1EA1ng n\u1ED9i dung tr\u1EA3 v\u1EC1 giao di\u1EC7n OTA kh\u00F4ng ch\u00EDnh x\u00E1c
|
||||
10108=X\u00E1c th\u1EF1c giao di\u1EC7n OTA th\u1EA5t b\u1EA1i
|
||||
10109=\u0110\u1ECBa ch\u1EC9 MCP kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10110=\u0110\u1ECBa ch\u1EC9 MCP kh\u00F4ng th\u1EC3 s\u1EED d\u1EE5ng localhost ho\u1EB7c 127.0.0.1
|
||||
10111=Kh\u00F4ng ph\u1EA3i \u0111\u1ECBa ch\u1EC9 MCP h\u1EE3p l\u1EC7
|
||||
10112=Truy c\u1EADp giao di\u1EC7n MCP th\u1EA5t b\u1EA1i
|
||||
10113=\u0110\u1ECBnh d\u1EA1ng n\u1ED9i dung tr\u1EA3 v\u1EC1 giao di\u1EC7n MCP kh\u00F4ng ch\u00EDnh x\u00E1c
|
||||
10114=X\u00E1c th\u1EF1c giao di\u1EC7n MCP th\u1EA5t b\u1EA1i
|
||||
10115=\u0110\u1ECBa ch\u1EC9 giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10116=\u0110\u1ECBa ch\u1EC9 giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i kh\u00F4ng th\u1EC3 s\u1EED d\u1EE5ng localhost ho\u1EB7c 127.0.0.1
|
||||
10117=Kh\u00F4ng ph\u1EA3i \u0111\u1ECBa ch\u1EC9 giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i h\u1EE3p l\u1EC7
|
||||
10118=\u0110\u1ECBa ch\u1EC9 giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i ph\u1EA3i b\u1EAFt \u0111\u1EA7u b\u1EB1ng http ho\u1EB7c https
|
||||
10119=Truy c\u1EADp giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i th\u1EA5t b\u1EA1i
|
||||
10120=\u0110\u1ECBnh d\u1EA1ng n\u1ED9i dung tr\u1EA3 v\u1EC1 giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i kh\u00F4ng ch\u00EDnh x\u00E1c
|
||||
10121=X\u00E1c th\u1EF1c giao di\u1EC7n d\u1EA5u gi\u1ECDng n\u00F3i th\u1EA5t b\u1EA1i
|
||||
10122=B\u00ED m\u1EADt MQTT kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10123=B\u00ED m\u1EADt MQTT c\u1EE7a b\u1EA1n kh\u00F4ng an to\u00E0n, c\u1EA7n \u00EDt nh\u1EA5t 8 k\u00FD t\u1EF1 v\u00E0 ph\u1EA3i ch\u1EE9a c\u1EA3 ch\u1EEF hoa v\u00E0 ch\u1EEF th\u01B0\u1EDDng
|
||||
10124=B\u00ED m\u1EADt MQTT c\u1EE7a b\u1EA1n kh\u00F4ng an to\u00E0n, b\u00ED m\u1EADt MQTT ph\u1EA3i ch\u1EE9a c\u1EA3 ch\u1EEF hoa v\u00E0 ch\u1EEF th\u01B0\u1EDDng
|
||||
10125=B\u00ED m\u1EADt MQTT c\u1EE7a b\u1EA1n ch\u1EE9a m\u1EADt kh\u1EA9u y\u1EBFu
|
||||
10128=Nh\u00E3n t\u1EEB \u0111i\u1EC3n b\u1ECB tr\u00F9ng l\u1EB7p
|
||||
10129=Kh\u00F3a SM2 ch\u01B0a \u0111\u01B0\u1EE3c c\u1EA5u h\u00ECnh
|
||||
10130=Gi\u1EA3i m\u00E3 SM2 th\u1EA5t b\u1EA1i
|
||||
10131=modelType v\u00E0 provideCode kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10132=Kh\u00F4ng c\u00F3 quy\u1EC1n xem l\u1ECBch s\u1EED tr\u00F2 chuy\u1EC7n c\u1EE7a agent n\u00E0y
|
||||
10133=ID phi\u00EAn kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10134=ID agent kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10135=T\u1EA3i xu\u1ED1ng l\u1ECBch s\u1EED tr\u00F2 chuy\u1EC7n th\u1EA5t b\u1EA1i
|
||||
10136=Li\u00EAn k\u1EBFt t\u1EA3i xu\u1ED1ng \u0111\u00E3 h\u1EBFt h\u1EA1n ho\u1EB7c kh\u00F4ng h\u1EE3p l\u1EC7
|
||||
10137=Li\u00EAn k\u1EBFt t\u1EA3i xu\u1ED1ng kh\u00F4ng h\u1EE3p l\u1EC7
|
||||
10138=Ng\u01B0\u1EDDi d\u00F9ng
|
||||
10139=Agent
|
||||
10140=Tệp âm thanh không thể để trống
|
||||
10141=Chỉ hỗ trợ tệp âm thanh
|
||||
10142=Kích thước tệp âm thanh không thể vượt quá 10MB
|
||||
10143=Tải lên thất bại
|
||||
10144=Bản ghi nhân bản giọng nói không tồn tại
|
||||
10145=Thông tin tài nguyên giọng nói không thể để trống
|
||||
10146=Tên nền tảng TTS không thể để trống
|
||||
10147=ID giọng nói không thể để trống
|
||||
10148=Tài khoản chủ sở hữu không thể để trống
|
||||
10149=ID tài nguyên giọng nói cần xóa không thể để trống
|
||||
10150=Bạn không có quyền thao tác bản ghi này
|
||||
10151=Vui lòng tải lên tệp âm thanh trước
|
||||
10152=Không tìm thấy cấu hình mô hình
|
||||
10153=Không tìm thấy loại mô hình
|
||||
10154=Đào tạo thất bại: {0}
|
||||
10155=Thiếu cấu hình Huoshan Engine
|
||||
10156=Lỗi định dạng phản hồi, thiếu trường BaseResp
|
||||
10157=Yêu cầu thất bại
|
||||
10158=Nhân bản âm sắc:
|
||||
10159=ID giọng nói đã tồn tại
|
||||
10160=Lỗi định dạng ID giọng nói Huoshan Engine, phải bắt đầu bằng S_
|
||||
10161=Địa chỉ Mac đã tồn tại
|
||||
10162=Nhà cung cấp mô hình không tồn tại
|
||||
10163=Bản ghi cơ sở kiến thức không tồn tại
|
||||
10164=Không tìm thấy cấu hình RAG
|
||||
10165=Lỗi loại cấu hình RAG
|
||||
10166=Không tìm thấy cấu hình RAG mặc định
|
||||
10167=Gọi API RAG thất bại: {0}
|
||||
10168=Tải lên tệp thất bại
|
||||
10169=Không có quyền thao tác cơ sở kiến thức này
|
||||
10170=Tên cơ sở kiến thức đã tồn tại
|
||||
10171=Cấu hình RAG base_url không thể để trống
|
||||
10172=Cấu hình RAG api_key không thể để trống
|
||||
10173=Cấu hình RAG api_key không thể chứa trình giữ chỗ, vui lòng thay thế bằng khóa API thực tế
|
||||
10174=Lỗi định dạng cấu hình RAG base_url, phải bắt đầu bằng http hoặc https
|
||||
10175=Địa chỉ Mac không thể để trống
|
||||
10176=Cấu hình RAG dataset_id không thể để trống
|
||||
10177=Cấu hình RAG model_id không thể để trống
|
||||
10178=Cấu hình RAG dataset_id và model_id không thể để trống
|
||||
10179=Tên tệp không thể để trống
|
||||
10180=Nội dung tệp không thể để trống
|
||||
10181=Tên nhân bản giọng nói không thể để trống
|
||||
10182=Không tìm thấy âm thanh nhân bản giọng nói
|
||||
10183=Không tìm thấy mẫu agent mặc định
|
||||
10184=Loại bộ chuyển đổi không được hỗ trợ
|
||||
10185=Kiểm tra cấu hình RAG thất bại
|
||||
10186=Tạo bộ chuyển đổi thất bại
|
||||
10187=Khởi tạo bộ chuyển đổi thất bại
|
||||
10188=Kiểm tra kết nối bộ chuyển đổi thất bại
|
||||
10189=Thao tác bộ chuyển đổi thất bại
|
||||
10190=Không tìm thấy bộ chuyển đổi
|
||||
10191=Lỗi bộ nhớ đệm bộ chuyển đổi
|
||||
10192=Không tìm thấy loại bộ chuyển đổi
|
||||
10140=T\u1EC7p \u00E2m thanh kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10141=Ch\u1EC9 h\u1ED7 tr\u1EE3 t\u1EC7p \u00E2m thanh
|
||||
10142=K\u00EDch th\u01B0\u1EDBc t\u1EC7p \u00E2m thanh kh\u00F4ng th\u1EC3 v\u01B0\u1EE3t qu\u00E1 10MB
|
||||
10143=T\u1EA3i l\u00EAn th\u1EA5t b\u1EA1i
|
||||
10144=B\u1EA3n ghi nh\u00E2n b\u1EA3n gi\u1ECDng n\u00F3i kh\u00F4ng t\u1ED3n t\u1EA1i
|
||||
10145=Th\u00F4ng tin t\u00E0i nguy\u00EAn gi\u1ECDng n\u00F3i kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10146=T\u00EAn n\u1EC1n t\u1EA3ng TTS kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10147=ID gi\u1ECDng n\u00F3i kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10148=T\u00E0i kho\u1EA3n ch\u1EE7 s\u1EDF h\u1EEFu kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10149=ID t\u00E0i nguy\u00EAn gi\u1ECDng n\u00F3i c\u1EA7n x\u00F3a kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10150=B\u1EA1n kh\u00F4ng c\u00F3 quy\u1EC1n thao t\u00E1c b\u1EA3n ghi n\u00E0y
|
||||
10151=Vui l\u00F2ng t\u1EA3i l\u00EAn t\u1EC7p \u00E2m thanh tr\u01B0\u1EDBc
|
||||
10152=Kh\u00F4ng t\u00ECm th\u1EA5y c\u1EA5u h\u00ECnh m\u00F4 h\u00ECnh
|
||||
10153=Kh\u00F4ng t\u00ECm th\u1EA5y lo\u1EA1i m\u00F4 h\u00ECnh
|
||||
10154=\u0110\u00E0o t\u1EA1o th\u1EA5t b\u1EA1i: {0}
|
||||
10155=Thi\u1EBFu c\u1EA5u h\u00ECnh Huoshan Engine
|
||||
10156=L\u1ED7i \u0111\u1ECBnh d\u1EA1ng ph\u1EA3n h\u1ED3i, thi\u1EBFu tr\u01B0\u1EDDng BaseResp
|
||||
10157=Y\u00EAu c\u1EA7u th\u1EA5t b\u1EA1i
|
||||
10158=Nh\u00E2n b\u1EA3n \u00E2m s\u1EAFc:
|
||||
10159=ID gi\u1ECDng n\u00F3i \u0111\u00E3 t\u1ED3n t\u1EA1i
|
||||
10160=L\u1ED7i \u0111\u1ECBnh d\u1EA1ng ID gi\u1ECDng n\u00F3i Huoshan Engine, ph\u1EA3i b\u1EAFt \u0111\u1EA7u b\u1EB1ng S_
|
||||
10161=\u0110\u1ECBa ch\u1EC9 Mac \u0111\u00E3 t\u1ED3n t\u1EA1i
|
||||
10162=Nh\u00E0 cung c\u1EA5p m\u00F4 h\u00ECnh kh\u00F4ng t\u1ED3n t\u1EA1i
|
||||
10163=B\u1EA3n ghi c\u01A1 s\u1EDF ki\u1EBFn th\u1EE9c kh\u00F4ng t\u1ED3n t\u1EA1i
|
||||
10164=Kh\u00F4ng t\u00ECm th\u1EA5y c\u1EA5u h\u00ECnh RAG
|
||||
10165=L\u1ED7i lo\u1EA1i c\u1EA5u h\u00ECnh RAG
|
||||
10166=Kh\u00F4ng t\u00ECm th\u1EA5y c\u1EA5u h\u00ECnh RAG m\u1EB7c \u0111\u1ECBnh
|
||||
10167=G\u1ECDi API RAG th\u1EA5t b\u1EA1i: {0}
|
||||
10168=T\u1EA3i l\u00EAn t\u1EC7p th\u1EA5t b\u1EA1i
|
||||
10169=Kh\u00F4ng c\u00F3 quy\u1EC1n thao t\u00E1c c\u01A1 s\u1EDF ki\u1EBFn th\u1EE9c n\u00E0y
|
||||
10170=T\u00EAn c\u01A1 s\u1EDF ki\u1EBFn th\u1EE9c \u0111\u00E3 t\u1ED3n t\u1EA1i
|
||||
10171=C\u1EA5u h\u00ECnh RAG base_url kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10172=C\u1EA5u h\u00ECnh RAG api_key kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10173=C\u1EA5u h\u00ECnh RAG api_key kh\u00F4ng th\u1EC3 ch\u1EE9a tr\u00ECnh gi\u1EEF ch\u1ED7, vui l\u00F2ng thay th\u1EBF b\u1EB1ng kh\u00F3a API th\u1EF1c t\u1EBF
|
||||
10174=L\u1ED7i \u0111\u1ECBnh d\u1EA1ng c\u1EA5u h\u00ECnh RAG base_url, ph\u1EA3i b\u1EAFt \u0111\u1EA7u b\u1EB1ng http ho\u1EB7c https
|
||||
10175=\u0110\u1ECBa ch\u1EC9 Mac kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10176=C\u1EA5u h\u00ECnh RAG dataset_id kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10177=C\u1EA5u h\u00ECnh RAG model_id kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10178=C\u1EA5u h\u00ECnh RAG dataset_id v\u00E0 model_id kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10179=T\u00EAn t\u1EC7p kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10180=N\u1ED9i dung t\u1EC7p kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10181=T\u00EAn nh\u00E2n b\u1EA3n gi\u1ECDng n\u00F3i kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
|
||||
10182=Kh\u00F4ng t\u00ECm th\u1EA5y \u00E2m thanh nh\u00E2n b\u1EA3n gi\u1ECDng n\u00F3i
|
||||
10183=Kh\u00F4ng t\u00ECm th\u1EA5y m\u1EABu agent m\u1EB7c \u0111\u1ECBnh
|
||||
10184=Lo\u1EA1i b\u1ED9 chuy\u1EC3n \u0111\u1ED5i kh\u00F4ng \u0111\u01B0\u1EE3c h\u1ED7 tr\u1EE3
|
||||
10185=Ki\u1EC3m tra c\u1EA5u h\u00ECnh RAG th\u1EA5t b\u1EA1i
|
||||
10186=T\u1EA1o b\u1ED9 chuy\u1EC3n \u0111\u1ED5i th\u1EA5t b\u1EA1i
|
||||
10187=Kh\u1EDFi t\u1EA1o b\u1ED9 chuy\u1EC3n \u0111\u1ED5i th\u1EA5t b\u1EA1i
|
||||
10188=Ki\u1EC3m tra k\u1EBFt n\u1ED1i b\u1ED9 chuy\u1EC3n \u0111\u1ED5i th\u1EA5t b\u1EA1i
|
||||
10189=Thao t\u00E1c b\u1ED9 chuy\u1EC3n \u0111\u1ED5i th\u1EA5t b\u1EA1i
|
||||
10190=Kh\u00F4ng t\u00ECm th\u1EA5y b\u1ED9 chuy\u1EC3n \u0111\u1ED5i
|
||||
10191=L\u1ED7i b\u1ED9 nh\u1EDB \u0111\u1EC7m b\u1ED9 chuy\u1EC3n \u0111\u1ED5i
|
||||
10192=Kh\u00F4ng t\u00ECm th\u1EA5y lo\u1EA1i b\u1ED9 chuy\u1EC3n \u0111\u1ED5i
|
||||
10193=ID thi\u1EBFt b\u1ECB kh\u00F4ng th\u1EC3 tr\u1ED1ng
|
||||
10194=Kh\u00F4ng t\u00ECm th\u1EA5y thi\u1EBFt b\u1ECB ho\u1EB7c thi\u1EBFt b\u1ECB kh\u00F4ng tr\u1EF1c tuy\u1EBFn
|
||||
10195=S\u1ED1 l\u1EA7n t\u1EA3i l\u00EAn OTA v\u01B0\u1EE3t qu\u00E1 gi\u1EDBi h\u1EA1n
|
||||
@@ -198,4 +198,7 @@
|
||||
10189=\u9002\u914D\u5668\u64CD\u4F5C\u5931\u8D25
|
||||
10190=\u9002\u914D\u5668\u672A\u627E\u5230
|
||||
10191=\u9002\u914D\u5668\u7F13\u5B58\u9519\u8BEF
|
||||
10192=\u9002\u914D\u5668\u7C7B\u578B\u672A\u627E\u5230
|
||||
10192=\u9002\u914D\u5668\u7C7B\u578B\u672A\u627E\u5230
|
||||
10193=\u8BBE\u5907ID\u4E0D\u80FD\u4E3A\u7A7A
|
||||
10194=\u8BBE\u5907\u4E0D\u5B58\u5728\u6216\u4E0D\u5728\u7EBF
|
||||
10195=OTA\u4E0A\u4F20\u6B21\u6570\u8D85\u8FC7\u9650\u5236
|
||||
@@ -164,7 +164,7 @@
|
||||
10155=\u706B\u5C71\u5F15\u64CE\u7F3A\u5C11appid\u6216access_token
|
||||
10156=\u97FF\u61C9\u683C\u5F0F\u932F\u8AA4\uFF0C\u7F3A\u5C11BaseResp\u5B57\u6BB5
|
||||
10157=\u8ACB\u6C42\u5931\u6557
|
||||
10158=\u514b\u9686\u97f3\u8272:
|
||||
10158=\u514B\u9686\u97F3\u8272:
|
||||
10159=\u97F3\u8272ID\u5DF2\u5B58\u5728
|
||||
10160=\u706B\u5C71\u5F15\u64CE\u97F3\u8272ID\u683C\u5F0F\u932F\u8AA4\uFF0C\u5FC5\u9808\u4EE5S_\u958B\u982D
|
||||
10161=Mac\u5730\u5740\u5DF2\u5B58\u5728
|
||||
@@ -173,23 +173,23 @@
|
||||
10164=RAG\u914D\u7F6E\u672A\u627E\u5230
|
||||
10165=RAG\u914D\u7F6E\u985E\u578B\u932F\u8AA4
|
||||
10166=\u9810\u8A2DRAG\u914D\u7F6E\u672A\u627E\u5230
|
||||
10167=\u0052\u0041\u0047\u8abf\u7528\u5931\u6557\uFF0C{0}
|
||||
10167=RAG\u8ABF\u7528\u5931\u6557\uFF0C{0}
|
||||
10168=\u4E0A\u50B3\u6587\u4EF6\u5931\u6557
|
||||
10169=\u60A8\u6C92\u6709\u6B0A\u9650\u64CD\u4F5C\u8A72\u8A18\u9304
|
||||
10170=\u77E5\u8B58\u5EAB\u540D\u7A31\u91CD\u8907
|
||||
10171=\u0052\u0041\u0047\u914D\u7F6E\u4F53\u7684base_url\u4E0D\u80FD\u4E3A\u7A7A
|
||||
10172=\u0052\u0041\u0047\u914D\u7F6E\u4F53\u7684api_key\u4E0D\u80FD\u4E3A\u7A7A
|
||||
10173=\u0052\u0041\u0047\u914D\u7F6E\u4F53\u7684api_key\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u8BF7\u66F4\u6362\u4E3A\u5728\u53D6\u7684API\u53C2\u6570
|
||||
10174=\u0052\u0041\u0047\u914D\u7F6E\u4F53\u7684base_url\u683C\u5F0F\u9519\u8BEF\uFF0C\u5FC5\u987B\u4EE5http\u6216https\u5F00\u5934
|
||||
10171=RAG\u914D\u7F6E\u4F53\u7684base_url\u4E0D\u80FD\u4E3A\u7A7A
|
||||
10172=RAG\u914D\u7F6E\u4F53\u7684api_key\u4E0D\u80FD\u4E3A\u7A7A
|
||||
10173=RAG\u914D\u7F6E\u4F53\u7684api_key\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u8BF7\u66F4\u6362\u4E3A\u5728\u53D6\u7684API\u53C2\u6570
|
||||
10174=RAG\u914D\u7F6E\u4F53\u7684base_url\u683C\u5F0F\u9519\u8BEF\uFF0C\u5FC5\u987B\u4EE5http\u6216https\u5F00\u5934
|
||||
10175=mac\u5730\u5740\u4E0D\u80FD\u4E3A\u7A7A
|
||||
10176=\u0052\u0041\u0047\u914D\u7F6E\u4F53\u7684dataset_id\u4E0D\u80FD\u4E3A\u7A7A
|
||||
10177=\u0052\u0041\u0047\u914D\u7F6E\u4F53\u7684model_id\u4E0D\u80FD\u4E3A\u7A7A
|
||||
10178=\u0052\u0041\u0047\u914D\u7F6E\u4F53\u7684dataset_id\u548Cmodel_id\u4E0D\u80FD\u4E3A\u7A7A
|
||||
10179=\u6587\u4ef6\u540d\u7a31\u4e0d\u80fd\u70ba\u7a7a
|
||||
10180=\u6587\u4ef6\u5185\u5bb9\u4e0d\u80fd\u70ba\u7a7a
|
||||
10181=\u97f3\u8272\u514b\u9686\u540d\u7a31\u4e0d\u80fd\u70ba\u7a7a
|
||||
10182=\u97f3\u8272\u514b\u9686\u97f3\u983b\u4e0d\u5b58\u5728
|
||||
10183=\u9ed8\u8ba4\u667a\u80fd\u4f53\u672a\u627e\u5230
|
||||
10176=RAG\u914D\u7F6E\u4F53\u7684dataset_id\u4E0D\u80FD\u4E3A\u7A7A
|
||||
10177=RAG\u914D\u7F6E\u4F53\u7684model_id\u4E0D\u80FD\u4E3A\u7A7A
|
||||
10178=RAG\u914D\u7F6E\u4F53\u7684dataset_id\u548Cmodel_id\u4E0D\u80FD\u4E3A\u7A7A
|
||||
10179=\u6587\u4EF6\u540D\u7A31\u4E0D\u80FD\u70BA\u7A7A
|
||||
10180=\u6587\u4EF6\u5185\u5BB9\u4E0D\u80FD\u70BA\u7A7A
|
||||
10181=\u97F3\u8272\u514B\u9686\u540D\u7A31\u4E0D\u80FD\u70BA\u7A7A
|
||||
10182=\u97F3\u8272\u514B\u9686\u97F3\u983B\u4E0D\u5B58\u5728
|
||||
10183=\u9ED8\u8BA4\u667A\u80FD\u4F53\u672A\u627E\u5230
|
||||
10184=\u4E0D\u652F\u6301\u7684\u9002\u914D\u5668\u985E\u578B
|
||||
10185=RAG\u914D\u7F6E\u9A57\u8B49\u5931\u6557
|
||||
10186=\u9002\u914D\u5668\u5275\u5EFA\u5931\u6557
|
||||
@@ -198,4 +198,7 @@
|
||||
10189=\u9002\u914D\u5668\u64CD\u4F5C\u5931\u6557
|
||||
10190=\u9002\u914D\u5668\u672A\u627E\u5230
|
||||
10191=\u9002\u914D\u5668\u7F13\u5B58\u932F\u8AA4
|
||||
10192=\u9002\u914D\u5668\u985E\u578B\u672A\u627E\u5230
|
||||
10192=\u9002\u914D\u5668\u985E\u578B\u672A\u627E\u5230
|
||||
10193=\u8A2D\u5099ID\u4E0D\u80FD\u4E3A\u7A7A
|
||||
10194=\u8A2D\u5099\u4E0D\u5B58\u5728\u6216\u672A\u5728\u7DDA
|
||||
10195=OTA\u4E0A\u4F20\u6B21\u6578\u8D85\u904E\u9650\u5236
|
||||
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
@@ -4,9 +4,10 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="icon" href="./favicon.ico">
|
||||
<title>Xiaozhi AI Customization</title>
|
||||
<script type="module" crossorigin src="./assets/index-FKVSBRAB.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-NXxBVrod.css">
|
||||
<script type="module" crossorigin src="./assets/index-B8r0c7xg.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-CrIJdTCK.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -95,6 +95,9 @@ export default {
|
||||
RequestService.clearRequestTime();
|
||||
callback(res);
|
||||
})
|
||||
.fail((res) => {
|
||||
callback(res);
|
||||
})
|
||||
.networkFail((err) => {
|
||||
console.error('获取设备状态失败:', err);
|
||||
RequestService.reAjaxFun(() => {
|
||||
|
||||
@@ -167,7 +167,7 @@
|
||||
</div>
|
||||
|
||||
<img loading="lazy" alt="" src="@/assets/home/avatar.png" class="avatar-img" @click="handleAvatarClick" />
|
||||
<span class="el-dropdown-link" @click="handleAvatarClick">
|
||||
<span class="el-user-dropdown" @click="handleAvatarClick">
|
||||
{{ userInfo.username || "加载中..." }}
|
||||
<i class="el-icon-arrow-down el-icon--right" :class="{ 'rotate-down': userMenuVisible }"></i>
|
||||
</span>
|
||||
@@ -668,6 +668,7 @@ export default {
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
min-width: 120px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.logo-img {
|
||||
@@ -845,6 +846,9 @@ export default {
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
.el-user-dropdown {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 导航文本样式 - 支持中英文换行 */
|
||||
.nav-text {
|
||||
|
||||
@@ -1125,7 +1125,7 @@ export default {
|
||||
'voiceClone.training': 'Training',
|
||||
'voiceClone.trainSuccess': 'Training erfolgreich',
|
||||
'voiceClone.trainFailed': 'Training fehlgeschlagen',
|
||||
'voiceClone.itemsPerPage': '{items} Einträge pro Seite',
|
||||
'voiceClone.itemsPerPage': '{items} Einträge/Seite',
|
||||
'voiceClone.firstPage': 'Erste Seite',
|
||||
'voiceClone.prevPage': 'Vorherige Seite',
|
||||
'voiceClone.nextPage': 'Nächste Seite',
|
||||
|
||||
@@ -1125,7 +1125,7 @@ export default {
|
||||
'voiceClone.training': 'Training',
|
||||
'voiceClone.trainSuccess': 'Training successful',
|
||||
'voiceClone.trainFailed': 'Training failed',
|
||||
'voiceClone.itemsPerPage': '{items} items per page',
|
||||
'voiceClone.itemsPerPage': '{items} items/page',
|
||||
'voiceClone.firstPage': 'First Page',
|
||||
'voiceClone.prevPage': 'Previous Page',
|
||||
'voiceClone.nextPage': 'Next Page',
|
||||
|
||||
@@ -1125,7 +1125,7 @@ export default {
|
||||
'voiceClone.training': 'Đang huấn luyện',
|
||||
'voiceClone.trainSuccess': 'Huấn luyện thành công',
|
||||
'voiceClone.trainFailed': 'Huấn luyện thất bại',
|
||||
'voiceClone.itemsPerPage': '{items} mục mỗi trang',
|
||||
'voiceClone.itemsPerPage': '{items} mục/trang',
|
||||
'voiceClone.firstPage': 'Trang đầu',
|
||||
'voiceClone.prevPage': 'Trang trước',
|
||||
'voiceClone.nextPage': 'Trang sau',
|
||||
|
||||
@@ -1125,7 +1125,7 @@ export default {
|
||||
'voiceClone.training': '训练中',
|
||||
'voiceClone.trainSuccess': '训练成功',
|
||||
'voiceClone.trainFailed': '训练失败',
|
||||
'voiceClone.itemsPerPage': '每页 {items} 条',
|
||||
'voiceClone.itemsPerPage': '{items}条/页',
|
||||
'voiceClone.firstPage': '首页',
|
||||
'voiceClone.prevPage': '上一页',
|
||||
'voiceClone.nextPage': '下一页',
|
||||
|
||||
@@ -1125,7 +1125,7 @@ export default {
|
||||
'voiceClone.training': '訓練中',
|
||||
'voiceClone.trainSuccess': '訓練成功',
|
||||
'voiceClone.trainFailed': '訓練失敗',
|
||||
'voiceClone.itemsPerPage': '每頁 {items} 條',
|
||||
'voiceClone.itemsPerPage': '{items}條/頁',
|
||||
'voiceClone.firstPage': '首頁',
|
||||
'voiceClone.prevPage': '上一頁',
|
||||
'voiceClone.nextPage': '下一頁',
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
<el-button size="mini" type="text" @click="handleUnbind(scope.row.device_id)">
|
||||
{{ $t('device.unbind') }}
|
||||
</el-button>
|
||||
<el-button v-if="isGenerate(scope.row)" size="mini" type="text" @click="handleGenertor">
|
||||
<el-button v-if="isGenerate(scope.row)" size="mini" type="text" @click="handleGenertor(scope.row)">
|
||||
{{ $t('device.deviceThemeGeneration') }}
|
||||
</el-button>
|
||||
</template>
|
||||
@@ -337,10 +337,10 @@ export default {
|
||||
});
|
||||
});
|
||||
},
|
||||
handleGenertor() {
|
||||
handleGenertor(row) {
|
||||
const pathname = window.location.pathname;
|
||||
const basePath = pathname.split('/').slice(0, -1).join('/');
|
||||
const url = `${window.location.origin}${basePath}/generator/`;
|
||||
const url = `${window.location.origin}${basePath}/generator/?deviceId=${row.device_id}`;
|
||||
sessionStorage.setItem('devicePath', window.location.href);
|
||||
window.location.href = url;
|
||||
},
|
||||
@@ -395,7 +395,10 @@ export default {
|
||||
|
||||
// 获取设备状态
|
||||
fetchDeviceStatus(agentId) {
|
||||
// 开启表格等待状态,处理动态加载表头导致鼠标所在行的hover事件无法移除的问题
|
||||
this.loading = true;
|
||||
Api.device.getDeviceStatus(agentId, ({ data }) => {
|
||||
this.loading = false;
|
||||
if (data.code === 0) {
|
||||
try {
|
||||
// 解析后端返回的设备状态JSON
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
<div style="
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 15px;
|
||||
margin-left: 10px;
|
||||
margin-top: 11px;
|
||||
margin-left: 11px;
|
||||
gap: 10px;
|
||||
">
|
||||
<img loading="lazy" alt="" src="@/assets/xiaozhi-logo.png" style="width: 45px; height: 45px" />
|
||||
<img loading="lazy" alt="" :src="xiaozhiAiIcon" style="height: 18px" />
|
||||
<img loading="lazy" alt="" src="@/assets/xiaozhi-logo.png" style="width: 42px; height: 42px" />
|
||||
<img loading="lazy" alt="" :src="xiaozhiAiIcon" style="height: 20px" />
|
||||
</div>
|
||||
</el-header>
|
||||
<div class="login-person">
|
||||
|
||||
@@ -216,6 +216,18 @@ class TTSProvider(TTSProviderBase):
|
||||
|
||||
try:
|
||||
data = json.loads(json_str)
|
||||
|
||||
# 检查业务层错误
|
||||
base_resp = data.get("base_resp", {})
|
||||
status_code = base_resp.get("status_code", 0)
|
||||
if status_code != 0:
|
||||
status_msg = base_resp.get("status_msg", "未知错误")
|
||||
logger.bind(tag=TAG).error(
|
||||
f"TTS请求失败, 错误码:{status_code}, 错误消息:{status_msg}"
|
||||
)
|
||||
self.tts_audio_queue.put((SentenceType.LAST, [], None))
|
||||
return
|
||||
|
||||
status = data.get("data", {}).get("status", 1)
|
||||
audio_hex = data.get("data", {}).get("audio")
|
||||
|
||||
|
||||
@@ -193,18 +193,55 @@ body {
|
||||
}
|
||||
|
||||
/* ==================== 聊天消息流(弹幕样式) ==================== */
|
||||
.chat-stream {
|
||||
.chat-container {
|
||||
position: absolute;
|
||||
bottom: 100px;
|
||||
bottom: 150px;
|
||||
right: 20px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.chat-stream {
|
||||
width: 300px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
z-index: 100;
|
||||
pointer-events: none;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.chat-ipt {
|
||||
width: 100%;
|
||||
display: none;
|
||||
justify-content: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.chat-ipt input {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
border: none;
|
||||
border-radius: 24px;
|
||||
background: rgba(35, 39, 42, 0.85);
|
||||
backdrop-filter: blur(10px);
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.chat-ipt input::placeholder {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.chat-ipt input:focus {
|
||||
outline: none;
|
||||
border-color: rgba(88, 101, 242, 0.6);
|
||||
box-shadow: 0 4px 16px rgba(88, 101, 242, 0.3);
|
||||
background: rgba(45, 49, 52, 0.9);
|
||||
}
|
||||
|
||||
.chat-message {
|
||||
@@ -955,10 +992,10 @@ body {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.chat-stream {
|
||||
width: 250px;
|
||||
.chat-container {
|
||||
width: 230px;
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
bottom: 105px;
|
||||
}
|
||||
|
||||
.message-bubble {
|
||||
|
||||
@@ -43,14 +43,6 @@ class App {
|
||||
log('应用初始化完成', 'success');
|
||||
}
|
||||
|
||||
// 设置model加载状态
|
||||
setModelLoadingStatus(isLoading) {
|
||||
const modelLoading = document.getElementById('modelLoading');
|
||||
if (modelLoading) {
|
||||
modelLoading.style.display = isLoading ? 'flex' : 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化Live2D
|
||||
async initLive2D() {
|
||||
try {
|
||||
@@ -81,6 +73,14 @@ class App {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置model加载状态
|
||||
setModelLoadingStatus(isLoading) {
|
||||
const modelLoading = document.getElementById('modelLoading');
|
||||
if (modelLoading) {
|
||||
modelLoading.style.display = isLoading ? 'flex' : 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建并启动应用
|
||||
@@ -89,17 +89,10 @@ const app = new App();
|
||||
// 将应用实例暴露到全局,供其他模块访问
|
||||
window.chatApp = app;
|
||||
|
||||
// DOM加载完成后初始化
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => app.init());
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// 初始化应用
|
||||
app.init();
|
||||
window.addEventListener('beforeunload', () => {
|
||||
// 销毁定时器
|
||||
if (app.uiController && app.uiController.wsTimer) {
|
||||
clearInterval(app.uiController.wsTimer);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
export default app;
|
||||
|
||||
@@ -58,12 +58,16 @@ export function saveConfig() {
|
||||
|
||||
// 获取配置值
|
||||
export function getConfig() {
|
||||
const deviceMac = document.getElementById('deviceMac').value.trim();
|
||||
// 从DOM获取值
|
||||
const deviceMac = document.getElementById('deviceMac')?.value.trim() || '';
|
||||
const deviceName = document.getElementById('deviceName')?.value.trim() || '';
|
||||
const clientId = document.getElementById('clientId')?.value.trim() || '';
|
||||
|
||||
return {
|
||||
deviceId: deviceMac, // 使用MAC地址作为deviceId
|
||||
deviceName: document.getElementById('deviceName').value.trim(),
|
||||
deviceMac: deviceMac,
|
||||
clientId: document.getElementById('clientId').value.trim()
|
||||
deviceName,
|
||||
deviceMac,
|
||||
clientId
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { log } from '../../utils/logger.js';
|
||||
import { updateScriptStatus } from '../../ui/dom-helper.js'
|
||||
|
||||
|
||||
// 检查Opus库是否已加载
|
||||
@@ -15,7 +14,6 @@ export function checkOpusLoaded() {
|
||||
// 使用Module.instance对象替换全局Module对象
|
||||
window.ModuleInstance = Module.instance;
|
||||
log('Opus库加载成功(使用Module.instance)', 'success');
|
||||
updateScriptStatus('Opus库加载成功', 'success');
|
||||
|
||||
// 3秒后隐藏状态
|
||||
const statusElement = document.getElementById('scriptStatus');
|
||||
@@ -27,7 +25,6 @@ export function checkOpusLoaded() {
|
||||
if (typeof Module._opus_decoder_get_size === 'function') {
|
||||
window.ModuleInstance = Module;
|
||||
log('Opus库加载成功(使用全局Module)', 'success');
|
||||
updateScriptStatus('Opus库加载成功', 'success');
|
||||
|
||||
// 3秒后隐藏状态
|
||||
const statusElement = document.getElementById('scriptStatus');
|
||||
@@ -38,7 +35,6 @@ export function checkOpusLoaded() {
|
||||
throw new Error('Opus解码函数未找到,可能Module结构不正确');
|
||||
} catch (err) {
|
||||
log(`Opus库加载失败,请检查libopus.js文件是否存在且正确: ${err.message}`, 'error');
|
||||
updateScriptStatus('Opus库加载失败,请检查libopus.js文件是否存在且正确', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -307,14 +307,7 @@ export class AudioRecorder {
|
||||
|
||||
// 发送监听开始消息
|
||||
if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
|
||||
const listenMessage = {
|
||||
type: 'listen',
|
||||
mode: 'manual',
|
||||
state: 'start'
|
||||
};
|
||||
|
||||
log(`发送录音开始消息: ${JSON.stringify(listenMessage)}`, 'info');
|
||||
this.websocket.send(JSON.stringify(listenMessage));
|
||||
log(`发送录音开始消息`, 'info');
|
||||
} else {
|
||||
log('WebSocket未连接,无法发送开始消息', 'error');
|
||||
return false;
|
||||
@@ -403,14 +396,6 @@ export class AudioRecorder {
|
||||
if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
|
||||
const emptyOpusFrame = new Uint8Array(0);
|
||||
this.websocket.send(emptyOpusFrame);
|
||||
|
||||
const stopMessage = {
|
||||
type: 'listen',
|
||||
mode: 'manual',
|
||||
state: 'stop'
|
||||
};
|
||||
|
||||
this.websocket.send(JSON.stringify(stopMessage));
|
||||
log('已发送录音停止信号', 'info');
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { otaStatusStyle } from '../../ui/dom-helper.js';
|
||||
import { log } from '../../utils/logger.js';
|
||||
|
||||
// WebSocket 连接
|
||||
@@ -62,18 +61,6 @@ function validateConfig(config) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 判断wsUrl路径是否存在错误
|
||||
function validateWsUrl(wsUrl) {
|
||||
if (wsUrl === '') return false;
|
||||
// 检查URL格式
|
||||
if (!wsUrl.startsWith('ws://') && !wsUrl.startsWith('wss://')) {
|
||||
log('URL格式错误,必须以ws://或wss://开头', 'error');
|
||||
return false;
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
// OTA发送请求,验证状态,并返回响应数据
|
||||
async function sendOTA(otaUrl, config) {
|
||||
try {
|
||||
@@ -96,7 +83,7 @@ async function sendOTA(otaUrl, config) {
|
||||
},
|
||||
ota: { label: 'xiaozhi-web-test' },
|
||||
board: {
|
||||
type: 'xiaozhi-web-test',
|
||||
type: config.deviceName,
|
||||
ssid: 'xiaozhi-web-test',
|
||||
rssi: 0,
|
||||
channel: 0,
|
||||
@@ -115,10 +102,8 @@ async function sendOTA(otaUrl, config) {
|
||||
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
|
||||
|
||||
const result = await res.json();
|
||||
otaStatusStyle(true)
|
||||
return result; // 返回完整的响应数据
|
||||
} catch (err) {
|
||||
otaStatusStyle(false)
|
||||
return null; // 失败返回null
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
// WebSocket消息处理模块
|
||||
import { log } from '../../utils/logger.js';
|
||||
import { webSocketConnect } from './ota-connector.js';
|
||||
import { getConfig, saveConnectionUrls } from '../../config/manager.js';
|
||||
import { uiController } from '../../ui/controller.js';
|
||||
import { log } from '../../utils/logger.js';
|
||||
import { getAudioPlayer } from '../audio/player.js';
|
||||
import { getAudioRecorder } from '../audio/recorder.js';
|
||||
import { getMcpTools, executeMcpTool, setWebSocket as setMcpWebSocket } from '../mcp/tools.js';
|
||||
import { executeMcpTool, getMcpTools, setWebSocket as setMcpWebSocket } from '../mcp/tools.js';
|
||||
import { webSocketConnect } from './ota-connector.js';
|
||||
|
||||
// WebSocket处理器类
|
||||
export class WebSocketHandler {
|
||||
@@ -73,6 +74,7 @@ export class WebSocketHandler {
|
||||
handleTextMessage(message) {
|
||||
if (message.type === 'hello') {
|
||||
log(`服务器回应:${JSON.stringify(message, null, 2)}`, 'success');
|
||||
uiController.startAIChatSession();
|
||||
} else if (message.type === 'tts') {
|
||||
this.handleTTSMessage(message);
|
||||
} else if (message.type === 'audio') {
|
||||
@@ -99,10 +101,10 @@ export class WebSocketHandler {
|
||||
}
|
||||
|
||||
// 触发Live2D情绪动作
|
||||
if (message.emotion) {
|
||||
console.log(`收到情绪消息: emotion=${message.emotion}, text=${message.text}`);
|
||||
this.triggerLive2DEmotionAction(message.emotion);
|
||||
}
|
||||
if (message.emotion) {
|
||||
console.log(`收到情绪消息: emotion=${message.emotion}, text=${message.text}`);
|
||||
this.triggerLive2DEmotionAction(message.emotion);
|
||||
}
|
||||
}
|
||||
|
||||
// 只有当文本不仅仅是表情时,才添加到对话中
|
||||
@@ -271,6 +273,26 @@ export class WebSocketHandler {
|
||||
this.websocket.send(replyMessage);
|
||||
} else if (payload.method === 'initialize') {
|
||||
log(`收到工具初始化请求: ${JSON.stringify(payload.params)}`, 'info');
|
||||
const replyMessage = JSON.stringify({
|
||||
"session_id": message.session_id || "",
|
||||
"type": "mcp",
|
||||
"payload": {
|
||||
"jsonrpc": "2.0",
|
||||
"id": payload.id,
|
||||
"result": {
|
||||
"protocolVersion": "2024-11-05",
|
||||
"capabilities": {
|
||||
"tools": {}
|
||||
},
|
||||
"serverInfo": {
|
||||
"name": "xiaozhi-web-test",
|
||||
"version": "2.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
log(`回复初始化响应`, 'info');
|
||||
this.websocket.send(replyMessage);
|
||||
} else {
|
||||
log(`未知的MCP方法: ${payload.method}`, 'warning');
|
||||
}
|
||||
@@ -421,7 +443,6 @@ export class WebSocketHandler {
|
||||
|
||||
const listenMessage = {
|
||||
type: 'listen',
|
||||
mode: 'manual',
|
||||
state: 'detect',
|
||||
text: text
|
||||
};
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// 背景图加载检测
|
||||
(function() {
|
||||
const backgroundContainer = document.getElementById('backgroundContainer');
|
||||
|
||||
// 提取背景图片URL
|
||||
let bgImageUrl = window.getComputedStyle(backgroundContainer).backgroundImage;
|
||||
const urlMatch = bgImageUrl && bgImageUrl.match(/url\(["']?(.*?)["']?\)/);
|
||||
|
||||
if (!urlMatch || !urlMatch[1]) {
|
||||
console.warn('未提取到有效的背景图片URL');
|
||||
return;
|
||||
}
|
||||
|
||||
bgImageUrl = urlMatch[1];
|
||||
|
||||
const bgImage = new Image();
|
||||
bgImage.onerror = function() {
|
||||
console.error('背景图片加载失败:', bgImageUrl);
|
||||
};
|
||||
|
||||
// 加载成功显示模型加载
|
||||
bgImage.onload = function() {
|
||||
modelLoading.style.display = 'flex';
|
||||
};
|
||||
|
||||
bgImage.src = bgImageUrl;
|
||||
})();
|
||||
@@ -1,15 +0,0 @@
|
||||
// 背景图加载检测
|
||||
const backgroundContainer = document.getElementById('backgroundContainer');
|
||||
// 提取背景图片URL
|
||||
let bgImageUrl = window.getComputedStyle(backgroundContainer).backgroundImage;
|
||||
const urlMatch = bgImageUrl.match(/url\(["']?(.*?)["']?\)/);
|
||||
bgImageUrl = urlMatch[1];
|
||||
const bgImage = new Image();
|
||||
bgImage.src = bgImageUrl;
|
||||
// 加载完成后显示模型加载
|
||||
bgImage.onload = function () {
|
||||
const modelLoading = document.getElementById('modelLoading');
|
||||
if (modelLoading) {
|
||||
modelLoading.style.display = 'flex';
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// UI控制模块
|
||||
import { loadConfig, saveConfig, getConfig } from '../config/manager.js';
|
||||
import { loadConfig, saveConfig } from '../config/manager.js';
|
||||
import { getAudioRecorder } from '../core/audio/recorder.js';
|
||||
import { getWebSocketHandler } from '../core/network/websocket.js';
|
||||
import { getAudioPlayer } from '../core/audio/player.js';
|
||||
@@ -11,7 +11,6 @@ class UIController {
|
||||
this.visualizerCanvas = null;
|
||||
this.visualizerContext = null;
|
||||
this.audioStatsTimer = null;
|
||||
this.wsTimer = null;
|
||||
this.currentBackgroundIndex = 0;
|
||||
this.backgroundImages = ['1.png', '2.png', '3.png'];
|
||||
|
||||
@@ -93,6 +92,7 @@ class UIController {
|
||||
if (isConnected) {
|
||||
wsHandler.disconnect();
|
||||
this.updateDialButton(false);
|
||||
this.addChatMessage('已断开连接,期待下次再见~😉', false);
|
||||
} else {
|
||||
// 检查OTA地址是否已填写
|
||||
const otaUrlInput = document.getElementById('otaUrl');
|
||||
@@ -133,12 +133,31 @@ class UIController {
|
||||
});
|
||||
}
|
||||
|
||||
// 消息输入框事件
|
||||
const chatIpt = document.getElementById('chatIpt');
|
||||
if (chatIpt) {
|
||||
const wsHandler = getWebSocketHandler();
|
||||
chatIpt.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
if (e.target.value) {
|
||||
wsHandler.sendTextMessage(e.target.value);
|
||||
e.target.value = '';
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 关闭按钮
|
||||
const closeButtons = document.querySelectorAll('.close-btn');
|
||||
closeButtons.forEach(btn => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
const modal = e.target.closest('.modal');
|
||||
if (modal) {
|
||||
if (modal.id === 'settingsModal') {
|
||||
saveConfig();
|
||||
}
|
||||
this.hideModal(modal.id);
|
||||
}
|
||||
});
|
||||
@@ -157,23 +176,19 @@ class UIController {
|
||||
modals.forEach(modal => {
|
||||
modal.addEventListener('click', (e) => {
|
||||
if (e.target === modal) {
|
||||
if (modal.id === 'settingsModal') {
|
||||
saveConfig();
|
||||
}
|
||||
this.hideModal(modal.id);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 保存配置按钮
|
||||
const saveConfigBtn = document.getElementById('saveConfigBtn');
|
||||
if (saveConfigBtn) {
|
||||
saveConfigBtn.addEventListener('click', () => {
|
||||
this.saveConfig();
|
||||
});
|
||||
}
|
||||
|
||||
// 添加MCP工具按钮
|
||||
const addMCPToolBtn = document.getElementById('addMCPToolBtn');
|
||||
if (addMCPToolBtn) {
|
||||
addMCPToolBtn.addEventListener('click', () => {
|
||||
addMCPToolBtn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
this.addMCPTool();
|
||||
});
|
||||
}
|
||||
@@ -247,7 +262,7 @@ class UIController {
|
||||
const recordBtn = document.getElementById('recordBtn');
|
||||
if (recordBtn) {
|
||||
if (isRecording) {
|
||||
recordBtn.querySelector('.btn-text').textContent = `录音中 ${seconds.toFixed(1)}秒`;
|
||||
recordBtn.querySelector('.btn-text').textContent = `录音中`;
|
||||
recordBtn.classList.add('recording');
|
||||
} else {
|
||||
recordBtn.querySelector('.btn-text').textContent = '录音';
|
||||
@@ -315,35 +330,14 @@ class UIController {
|
||||
}
|
||||
}
|
||||
|
||||
// 保存配置
|
||||
saveConfig() {
|
||||
const config = {
|
||||
serverUrl: document.getElementById('serverUrl').value,
|
||||
serverPort: document.getElementById('serverPort').value,
|
||||
audioDevice: document.getElementById('audioDevice').value,
|
||||
audioSampleRate: document.getElementById('audioSampleRate').value,
|
||||
audioChannels: document.getElementById('audioChannels').value
|
||||
};
|
||||
|
||||
saveConfig(config);
|
||||
this.hideModal('settingsModal');
|
||||
|
||||
// 显示保存成功消息
|
||||
this.addChatMessage('配置已保存', false);
|
||||
}
|
||||
|
||||
// 拨号成功后直接开始录音
|
||||
dialAndRecord() {
|
||||
// 连接成功后开始对话
|
||||
startAIChatSession() {
|
||||
this.addChatMessage('连接成功,开始聊天吧~🙂', false);
|
||||
// 开启录音
|
||||
const recordBtn = document.getElementById('recordBtn');
|
||||
const wsHandler = getWebSocketHandler();
|
||||
this.wsTimer = setInterval(() => {
|
||||
if (wsHandler.isConnected() && wsHandler.websocket.onopen) {
|
||||
clearInterval(this.wsTimer);
|
||||
this.wsTimer = null;
|
||||
recordBtn.click();
|
||||
return;
|
||||
}
|
||||
}, 500);
|
||||
if (recordBtn) {
|
||||
recordBtn.click();
|
||||
}
|
||||
}
|
||||
|
||||
// 处理连接按钮点击
|
||||
@@ -379,22 +373,18 @@ class UIController {
|
||||
// 显示连接中消息
|
||||
this.addChatMessage('正在连接服务器...', false);
|
||||
|
||||
const chatIpt = document.getElementById('chatIpt');
|
||||
if (chatIpt) {
|
||||
chatIpt.style.display = 'flex';
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取配置信息
|
||||
const config = getConfig();
|
||||
|
||||
// 导入OTA连接器
|
||||
const { webSocketConnect } = await import('../core/network/ota-connector.js');
|
||||
// 获取WebSocket处理器
|
||||
const wsHandler = getWebSocketHandler();
|
||||
const isConnected = await wsHandler.connect();
|
||||
|
||||
// 建立OTA连接
|
||||
const websocket = await webSocketConnect(otaUrl, config);
|
||||
|
||||
if (websocket) {
|
||||
// 获取WebSocket处理器
|
||||
const wsHandler = getWebSocketHandler();
|
||||
|
||||
// 设置WebSocket连接
|
||||
wsHandler.websocket = websocket;
|
||||
if (isConnected) {
|
||||
|
||||
// 设置连接状态回调
|
||||
wsHandler.onConnectionStateChange = (isConnected) => {
|
||||
@@ -430,16 +420,10 @@ class UIController {
|
||||
dialBtn.disabled = false;
|
||||
dialBtn.querySelector('.btn-text').textContent = '挂断';
|
||||
dialBtn.classList.add('dial-active');
|
||||
|
||||
this.dialAndRecord();
|
||||
}
|
||||
|
||||
this.hideModal('settingsModal');
|
||||
|
||||
// 自动尝试建立WebSocket连接
|
||||
setTimeout(() => {
|
||||
wsHandler.connect();
|
||||
}, 1000);
|
||||
} else {
|
||||
throw new Error('OTA连接失败');
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
// DOM元素
|
||||
const connectButton = document.getElementById('connectButton');
|
||||
const serverUrlInput = document.getElementById('serverUrl');
|
||||
const connectionStatus = document.getElementById('connectionStatus');
|
||||
const messageInput = document.getElementById('messageInput');
|
||||
const sendTextButton = document.getElementById('sendTextButton');
|
||||
const recordButton = document.getElementById('recordButton');
|
||||
const stopButton = document.getElementById('stopButton');
|
||||
// 会话记录
|
||||
const conversationDiv = document.getElementById('conversation');
|
||||
const logContainer = document.getElementById('logContainer');
|
||||
let visualizerCanvas = document.getElementById('audioVisualizer');
|
||||
|
||||
// ota 是否连接成功,修改成对应的样式
|
||||
export function otaStatusStyle(flan) {
|
||||
const otaStatusElement = document.getElementById('otaStatus');
|
||||
if (otaStatusElement) {
|
||||
if (flan) {
|
||||
otaStatusElement.textContent = 'OTA已连接';
|
||||
otaStatusElement.style.color = 'green';
|
||||
} else {
|
||||
otaStatusElement.textContent = 'OTA未连接';
|
||||
otaStatusElement.style.color = 'red';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ota 是否连接成功,修改成对应的样式
|
||||
export function getLogContainer(flan) {
|
||||
return logContainer;
|
||||
}
|
||||
|
||||
// 更新Opus库状态显示
|
||||
export function updateScriptStatus(message, type) {
|
||||
const statusElement = document.getElementById('scriptStatus');
|
||||
if (statusElement) {
|
||||
statusElement.textContent = message;
|
||||
statusElement.className = `script-status ${type}`;
|
||||
statusElement.style.display = 'block';
|
||||
statusElement.style.width = 'auto';
|
||||
}
|
||||
}
|
||||
|
||||
// 添加消息到会话记录
|
||||
export function addMessage(text, isUser = false) {
|
||||
const messageDiv = document.createElement('div');
|
||||
messageDiv.className = `message ${isUser ? 'user' : 'server'}`;
|
||||
messageDiv.textContent = text;
|
||||
conversationDiv.appendChild(messageDiv);
|
||||
conversationDiv.scrollTop = conversationDiv.scrollHeight;
|
||||
}
|
||||
|
||||
@@ -68,8 +68,13 @@
|
||||
<canvas id="live2d-stage"></canvas>
|
||||
|
||||
<!-- 聊天消息流(弹幕样式) -->
|
||||
<div class="chat-stream" id="chatStream">
|
||||
<!-- 聊天消息将动态插入这里 -->
|
||||
<div class="chat-container">
|
||||
<div class="chat-stream" id="chatStream">
|
||||
<!-- 聊天消息将动态插入这里 -->
|
||||
</div>
|
||||
<div class="chat-ipt" id="chatIpt">
|
||||
<input type="text" id="messageInput" autocomplete="off" placeholder="输入消息,按Enter发送">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部控制栏 -->
|
||||
@@ -128,7 +133,7 @@
|
||||
<div class="config-row">
|
||||
<div class="config-item">
|
||||
<label for="deviceMac">设备MAC:</label>
|
||||
<input type="text" id="deviceMac" placeholder="device-id" readonly>
|
||||
<input type="text" id="deviceMac" placeholder="device-id">
|
||||
</div>
|
||||
</div>
|
||||
<div class="config-row">
|
||||
@@ -138,7 +143,7 @@
|
||||
</div>
|
||||
<div class="config-item">
|
||||
<label for="deviceName">设备名称:</label>
|
||||
<input type="text" id="deviceName" value="Web测试设备" placeholder="deviceName">
|
||||
<input type="text" id="deviceName" value="Web测试设备" maxlength="50" placeholder="deviceName">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -231,8 +236,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 背景图加载检测 -->
|
||||
<script src="js/ui/bg-image-load-detector.js"></script>
|
||||
<!-- 背景加载 -->
|
||||
<script src="js/ui/background-load.js"></script>
|
||||
|
||||
<!-- PIXI.js 2D渲染引擎 -->
|
||||
<script src="js/live2d/pixi.js"></script>
|
||||
|
||||
Reference in New Issue
Block a user