This commit is contained in:
rainv123
2026-01-27 11:59:03 +08:00
44 changed files with 922 additions and 605 deletions
@@ -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;
}
}
@@ -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;
@@ -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)
@@ -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);
}
}
@@ -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);
}
@@ -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;
}
}
@@ -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=Trin 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=Gi SMS {0} thất bại
10026=Mu 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=Loi tham số không thể để trống
10036=Loi 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 hoc 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 hoc 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=Gi 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 trng, 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 hoc 127.0.0.1
10100=Định dạng địa chỉ WebSocket không chính xác
10101=Kim 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 hoc 127.0.0.1
10104=Địa chỉ OTA phải bắt đầu bằng http hoc https
10105=Địa chỉ OTA phải kết thúc bằng /ota/
10106=Truy cp giao din OTA thất bại
10107=Định dạng nội dung trả về giao din OTA không chính xác
10108=Xác thực giao din 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 hoc 127.0.0.1
10111=Không phải địa chỉ MCP hợp lệ
10112=Truy cp giao din MCP thất bại
10113=Định dạng nội dung trả về giao din MCP không chính xác
10114=Xác thực giao din 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 hoc 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 hoc https
10119=Truy cp 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=Bn 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=Bn 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=Gi 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 hoc 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=Ni 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=Kim 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=Kim 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
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

+3 -2
View File
@@ -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 {
+1 -1
View File
@@ -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',
+1 -1
View File
@@ -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',
+1 -1
View File
@@ -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',
+1 -1
View File
@@ -1125,7 +1125,7 @@ export default {
'voiceClone.training': '训练中',
'voiceClone.trainSuccess': '训练成功',
'voiceClone.trainFailed': '训练失败',
'voiceClone.itemsPerPage': '每页 {items} 条',
'voiceClone.itemsPerPage': '{items}条/页',
'voiceClone.firstPage': '首页',
'voiceClone.prevPage': '上一页',
'voiceClone.nextPage': '下一页',
+1 -1
View File
@@ -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
+4 -4
View File
@@ -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")
+43 -6
View File
@@ -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 {
+12 -19
View File
@@ -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';
}
}
+43 -59
View File
@@ -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;
}
+11 -6
View File
@@ -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>