mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-23 07:33:53 +08:00
Merge branch 'main' into py_test_tts
This commit is contained in:
@@ -232,7 +232,7 @@ public interface Constant {
|
||||
/**
|
||||
* 版本号
|
||||
*/
|
||||
public static final String VERSION = "0.6.1";
|
||||
public static final String VERSION = "0.6.3";
|
||||
|
||||
/**
|
||||
* 无效固件URL
|
||||
|
||||
+12
@@ -19,6 +19,8 @@ import xiaozhi.modules.agent.service.AgentChatAudioService;
|
||||
import xiaozhi.modules.agent.service.AgentChatHistoryService;
|
||||
import xiaozhi.modules.agent.service.AgentService;
|
||||
import xiaozhi.modules.agent.service.biz.AgentChatHistoryBizService;
|
||||
import xiaozhi.modules.device.entity.DeviceEntity;
|
||||
import xiaozhi.modules.device.service.DeviceService;
|
||||
|
||||
/**
|
||||
* {@link AgentChatHistoryBizService} impl
|
||||
@@ -35,6 +37,7 @@ public class AgentChatHistoryBizServiceImpl implements AgentChatHistoryBizServic
|
||||
private final AgentChatHistoryService agentChatHistoryService;
|
||||
private final AgentChatAudioService agentChatAudioService;
|
||||
private final RedisUtils redisUtils;
|
||||
private final DeviceService deviceService;
|
||||
|
||||
/**
|
||||
* 处理聊天记录上报,包括文件上传和相关信息记录
|
||||
@@ -68,6 +71,15 @@ public class AgentChatHistoryBizServiceImpl implements AgentChatHistoryBizServic
|
||||
|
||||
// 更新设备最后对话时间
|
||||
redisUtils.set(RedisKeys.getAgentDeviceLastConnectedAtById(agentId), new Date());
|
||||
|
||||
// 更新设备最后连接时间
|
||||
DeviceEntity device = deviceService.getDeviceByMacAddress(macAddress);
|
||||
if (device != null) {
|
||||
deviceService.updateDeviceConnectionInfo(agentId, device.getId(), null);
|
||||
} else {
|
||||
log.warn("聊天记录上报时,未找到mac地址为 {} 的设备", macAddress);
|
||||
}
|
||||
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
|
||||
+96
-30
@@ -5,6 +5,7 @@ import java.net.URISyntaxException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -18,7 +19,6 @@ import xiaozhi.common.utils.AESUtils;
|
||||
import xiaozhi.common.utils.HashEncryptionUtil;
|
||||
import xiaozhi.common.utils.JsonUtils;
|
||||
import xiaozhi.modules.agent.dto.McpJsonRpcRequest;
|
||||
import xiaozhi.modules.agent.dto.McpJsonRpcResponse;
|
||||
import xiaozhi.modules.agent.service.AgentMcpAccessPointService;
|
||||
import xiaozhi.modules.sys.service.SysParamsService;
|
||||
import xiaozhi.modules.sys.utils.WebSocketClientManager;
|
||||
@@ -61,53 +61,119 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
|
||||
wsUrl = wsUrl.replace("/mcp/", "/call/");
|
||||
|
||||
try {
|
||||
// 创建 WebSocket 连接
|
||||
// 创建 WebSocket 连接,增加超时时间到15秒
|
||||
try (WebSocketClientManager client = WebSocketClientManager.build(
|
||||
new WebSocketClientManager.Builder()
|
||||
.uri(wsUrl)
|
||||
.connectTimeout(5, TimeUnit.SECONDS)
|
||||
.maxSessionDuration(20, TimeUnit.SECONDS))) {
|
||||
.connectTimeout(8, TimeUnit.SECONDS)
|
||||
.maxSessionDuration(10, TimeUnit.SECONDS))) {
|
||||
|
||||
// 发送初始化通知
|
||||
McpJsonRpcRequest initRequest = new McpJsonRpcRequest("notifications/initialized");
|
||||
client.sendJson(initRequest);
|
||||
// 步骤1: 发送初始化消息并等待响应
|
||||
log.info("发送MCP初始化消息,智能体ID: {}", id);
|
||||
McpJsonRpcRequest initializeRequest = new McpJsonRpcRequest("initialize",
|
||||
Map.of(
|
||||
"protocolVersion", "2024-11-05",
|
||||
"capabilities", Map.of(
|
||||
"roots", Map.of("listChanged", false),
|
||||
"sampling", Map.of()),
|
||||
"clientInfo", Map.of(
|
||||
"name", "xz-mcp-broker",
|
||||
"version", "0.0.1")),
|
||||
1);
|
||||
client.sendJson(initializeRequest);
|
||||
|
||||
// 等待 0.2 秒
|
||||
Thread.sleep(200);
|
||||
|
||||
// 发送工具列表请求
|
||||
McpJsonRpcRequest toolsRequest = new McpJsonRpcRequest("tools/list", null, 1);
|
||||
client.sendJson(toolsRequest);
|
||||
|
||||
// 监听响应,直到收到包含 id=1 的响应
|
||||
List<String> responses = client.listener(response -> {
|
||||
// 等待初始化响应 (id=1) - 移除固定延迟,改为响应驱动
|
||||
List<String> initResponses = client.listenerWithoutClose(response -> {
|
||||
try {
|
||||
McpJsonRpcResponse jsonResponse = JsonUtils.parseObject(response, McpJsonRpcResponse.class);
|
||||
return jsonResponse != null && Integer.valueOf(1).equals(jsonResponse.getId());
|
||||
Map<String, Object> jsonMap = JsonUtils.parseObject(response, Map.class);
|
||||
if (jsonMap != null && Integer.valueOf(1).equals(jsonMap.get("id"))) {
|
||||
// 检查是否有result字段,表示初始化成功
|
||||
return jsonMap.containsKey("result") && !jsonMap.containsKey("error");
|
||||
}
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
log.warn("解析响应失败: {}", response, e);
|
||||
log.warn("解析初始化响应失败: {}", response, e);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// 处理响应
|
||||
for (String response : responses) {
|
||||
// 验证初始化响应
|
||||
boolean initSucceeded = false;
|
||||
for (String response : initResponses) {
|
||||
try {
|
||||
McpJsonRpcResponse jsonResponse = JsonUtils.parseObject(response, McpJsonRpcResponse.class);
|
||||
if (jsonResponse != null && Integer.valueOf(1).equals(jsonResponse.getId())
|
||||
&& jsonResponse.getResult() != null && jsonResponse.getResult().getTools() != null) {
|
||||
Map<String, Object> jsonMap = JsonUtils.parseObject(response, Map.class);
|
||||
if (jsonMap != null && Integer.valueOf(1).equals(jsonMap.get("id"))) {
|
||||
if (jsonMap.containsKey("result")) {
|
||||
log.info("MCP初始化成功,智能体ID: {}", id);
|
||||
initSucceeded = true;
|
||||
break;
|
||||
} else if (jsonMap.containsKey("error")) {
|
||||
log.error("MCP初始化失败,智能体ID: {}, 错误: {}", id, jsonMap.get("error"));
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("处理初始化响应失败: {}", response, e);
|
||||
}
|
||||
}
|
||||
|
||||
// 提取工具名称列表
|
||||
return java.util.Arrays.stream(jsonResponse.getResult().getTools())
|
||||
.map(McpJsonRpcResponse.McpTool::getName)
|
||||
.collect(Collectors.toList());
|
||||
if (!initSucceeded) {
|
||||
log.error("未收到有效的MCP初始化响应,智能体ID: {}", id);
|
||||
return List.of();
|
||||
}
|
||||
|
||||
// 步骤2: 发送初始化完成通知 - 只有在收到initialize响应后才发送
|
||||
log.info("发送MCP初始化完成通知,智能体ID: {}", id);
|
||||
String notificationJson = "{\"jsonrpc\":\"2.0\",\"method\":\"notifications/initialized\"}";
|
||||
client.sendText(notificationJson);
|
||||
|
||||
// 步骤3: 发送工具列表请求 - 立即发送,无需额外延迟
|
||||
log.info("发送MCP工具列表请求,智能体ID: {}", id);
|
||||
McpJsonRpcRequest toolsRequest = new McpJsonRpcRequest("tools/list", null, 2);
|
||||
client.sendJson(toolsRequest);
|
||||
|
||||
// 等待工具列表响应 (id=2)
|
||||
List<String> toolsResponses = client.listener(response -> {
|
||||
try {
|
||||
Map<String, Object> jsonMap = JsonUtils.parseObject(response, Map.class);
|
||||
return jsonMap != null && Integer.valueOf(2).equals(jsonMap.get("id"));
|
||||
} catch (Exception e) {
|
||||
log.warn("解析工具列表响应失败: {}", response, e);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// 处理工具列表响应
|
||||
for (String response : toolsResponses) {
|
||||
try {
|
||||
Map<String, Object> jsonMap = JsonUtils.parseObject(response, Map.class);
|
||||
if (jsonMap != null && Integer.valueOf(2).equals(jsonMap.get("id"))) {
|
||||
// 检查是否有result字段
|
||||
Object resultObj = jsonMap.get("result");
|
||||
if (resultObj instanceof Map) {
|
||||
Map<String, Object> resultMap = (Map<String, Object>) resultObj;
|
||||
Object toolsObj = resultMap.get("tools");
|
||||
if (toolsObj instanceof List) {
|
||||
List<Map<String, Object>> toolsList = (List<Map<String, Object>>) toolsObj;
|
||||
// 提取工具名称列表
|
||||
List<String> result = toolsList.stream()
|
||||
.map(tool -> (String) tool.get("name"))
|
||||
.filter(name -> name != null)
|
||||
.collect(Collectors.toList());
|
||||
log.info("成功获取MCP工具列表,智能体ID: {}, 工具数量: {}", id, result.size());
|
||||
return result;
|
||||
}
|
||||
} else if (jsonMap.containsKey("error")) {
|
||||
log.error("获取工具列表失败,智能体ID: {}, 错误: {}", id, jsonMap.get("error"));
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("处理工具列表响应失败: {}", response, e);
|
||||
}
|
||||
}
|
||||
|
||||
log.warn("未找到有效的工具列表响应");
|
||||
log.warn("未找到有效的工具列表响应,智能体ID: {}", id);
|
||||
return List.of();
|
||||
|
||||
}
|
||||
@@ -177,4 +243,4 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
|
||||
// 加密后成token值
|
||||
return AESUtils.encrypt(key, json);
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
-12
@@ -72,6 +72,8 @@ public class ConfigServiceImpl implements ConfigService {
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
agent.getVadModelId(),
|
||||
agent.getAsrModelId(),
|
||||
null,
|
||||
@@ -108,9 +110,13 @@ public class ConfigServiceImpl implements ConfigService {
|
||||
}
|
||||
// 获取音色信息
|
||||
String voice = null;
|
||||
String referenceAudio = null;
|
||||
String referenceText = null;
|
||||
TimbreDetailsVO timbre = timbreService.get(agent.getTtsVoiceId());
|
||||
if (timbre != null) {
|
||||
voice = timbre.getTtsVoice();
|
||||
referenceAudio = timbre.getReferenceAudio();
|
||||
referenceText = timbre.getReferenceText();
|
||||
}
|
||||
// 构建返回数据
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
@@ -163,6 +169,8 @@ public class ConfigServiceImpl implements ConfigService {
|
||||
agent.getSystemPrompt(),
|
||||
agent.getSummaryMemory(),
|
||||
voice,
|
||||
referenceAudio,
|
||||
referenceText,
|
||||
agent.getVadModelId(),
|
||||
agent.getAsrModelId(),
|
||||
agent.getLlmModelId(),
|
||||
@@ -179,7 +187,7 @@ public class ConfigServiceImpl implements ConfigService {
|
||||
/**
|
||||
* 构建配置信息
|
||||
*
|
||||
* @param paramsList 系统参数列表
|
||||
* @param config 系统参数列表
|
||||
* @return 配置信息
|
||||
*/
|
||||
private Object buildConfig(Map<String, Object> config) {
|
||||
@@ -250,21 +258,25 @@ public class ConfigServiceImpl implements ConfigService {
|
||||
/**
|
||||
* 构建模块配置
|
||||
*
|
||||
* @param prompt 提示词
|
||||
* @param voice 音色
|
||||
* @param vadModelId VAD模型ID
|
||||
* @param asrModelId ASR模型ID
|
||||
* @param llmModelId LLM模型ID
|
||||
* @param ttsModelId TTS模型ID
|
||||
* @param memModelId 记忆模型ID
|
||||
* @param intentModelId 意图模型ID
|
||||
* @param result 结果Map
|
||||
* @param prompt 提示词
|
||||
* @param voice 音色
|
||||
* @param referenceAudio 参考音频路径
|
||||
* @param referenceText 参考文本
|
||||
* @param vadModelId VAD模型ID
|
||||
* @param asrModelId ASR模型ID
|
||||
* @param llmModelId LLM模型ID
|
||||
* @param ttsModelId TTS模型ID
|
||||
* @param memModelId 记忆模型ID
|
||||
* @param intentModelId 意图模型ID
|
||||
* @param result 结果Map
|
||||
*/
|
||||
private void buildModuleConfig(
|
||||
String assistantName,
|
||||
String prompt,
|
||||
String summaryMemory,
|
||||
String voice,
|
||||
String referenceAudio,
|
||||
String referenceText,
|
||||
String vadModelId,
|
||||
String asrModelId,
|
||||
String llmModelId,
|
||||
@@ -286,12 +298,20 @@ public class ConfigServiceImpl implements ConfigService {
|
||||
continue;
|
||||
}
|
||||
ModelConfigEntity model = modelConfigService.getModelById(modelIds[i], isCache);
|
||||
if (model == null) {
|
||||
continue;
|
||||
}
|
||||
Map<String, Object> typeConfig = new HashMap<>();
|
||||
if (model.getConfigJson() != null) {
|
||||
typeConfig.put(model.getId(), model.getConfigJson());
|
||||
// 如果是TTS类型,添加private_voice属性
|
||||
if ("TTS".equals(modelTypes[i]) && voice != null) {
|
||||
((Map<String, Object>) model.getConfigJson()).put("private_voice", voice);
|
||||
if ("TTS".equals(modelTypes[i])) {
|
||||
if (voice != null)
|
||||
((Map<String, Object>) model.getConfigJson()).put("private_voice", voice);
|
||||
if (referenceAudio != null)
|
||||
((Map<String, Object>) model.getConfigJson()).put("ref_audio", referenceAudio);
|
||||
if (referenceText != null)
|
||||
((Map<String, Object>) model.getConfigJson()).put("ref_text", referenceText);
|
||||
}
|
||||
// 如果是Intent类型,且type=intent_llm,则给他添加附加模型
|
||||
if ("Intent".equals(modelTypes[i])) {
|
||||
|
||||
+10
@@ -25,6 +25,7 @@ import xiaozhi.common.utils.Result;
|
||||
import xiaozhi.modules.device.dto.DeviceRegisterDTO;
|
||||
import xiaozhi.modules.device.dto.DeviceUnBindDTO;
|
||||
import xiaozhi.modules.device.dto.DeviceUpdateDTO;
|
||||
import xiaozhi.modules.device.dto.DeviceManualAddDTO;
|
||||
import xiaozhi.modules.device.entity.DeviceEntity;
|
||||
import xiaozhi.modules.device.service.DeviceService;
|
||||
import xiaozhi.modules.security.user.SecurityUser;
|
||||
@@ -99,4 +100,13 @@ public class DeviceController {
|
||||
deviceService.updateById(entity);
|
||||
return new Result<Void>();
|
||||
}
|
||||
|
||||
@PostMapping("/manual-add")
|
||||
@Operation(summary = "手动添加设备")
|
||||
@RequiresPermissions("sys:role:normal")
|
||||
public Result<Void> manualAddDevice(@RequestBody @Valid DeviceManualAddDTO dto) {
|
||||
UserDetail user = SecurityUser.getUser();
|
||||
deviceService.manualAddDevice(user.getId(), dto);
|
||||
return new Result<>();
|
||||
}
|
||||
}
|
||||
@@ -51,13 +51,12 @@ public class OTAController {
|
||||
if (StringUtils.isBlank(clientId)) {
|
||||
clientId = deviceId;
|
||||
}
|
||||
String macAddress = deviceReportReqDTO.getMacAddress();
|
||||
boolean macAddressValid = isMacAddressValid(macAddress);
|
||||
boolean macAddressValid = isMacAddressValid(deviceId);
|
||||
// 设备Id和Mac地址应是一致的, 并且必须需要application字段
|
||||
if (!deviceId.equals(macAddress) || !macAddressValid || deviceReportReqDTO.getApplication() == null) {
|
||||
return createResponse(DeviceReportRespDTO.createError("Invalid OTA request"));
|
||||
if (!macAddressValid) {
|
||||
return createResponse(DeviceReportRespDTO.createError("Invalid device ID"));
|
||||
}
|
||||
return createResponse(deviceService.checkDeviceActive(macAddress, clientId, deviceReportReqDTO));
|
||||
return createResponse(deviceService.checkDeviceActive(deviceId, clientId, deviceReportReqDTO));
|
||||
}
|
||||
|
||||
@Operation(summary = "设备快速检查激活状态")
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package xiaozhi.modules.device.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class DeviceManualAddDTO {
|
||||
private String agentId;
|
||||
private String board; // 设备型号
|
||||
private String appVersion; // 固件版本
|
||||
private String macAddress; // Mac地址
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import xiaozhi.common.service.BaseService;
|
||||
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,5 +88,14 @@ public interface DeviceService extends BaseService<DeviceEntity> {
|
||||
*/
|
||||
Date getLatestLastConnectionTime(String agentId);
|
||||
|
||||
/**
|
||||
* 手动添加设备
|
||||
*/
|
||||
void manualAddDevice(Long userId, DeviceManualAddDTO dto);
|
||||
|
||||
/**
|
||||
* 更新设备连接信息
|
||||
*/
|
||||
void updateDeviceConnectionInfo(String agentId, String deviceId, String appVersion);
|
||||
|
||||
}
|
||||
+27
@@ -44,6 +44,7 @@ import xiaozhi.modules.device.vo.UserShowDeviceListVO;
|
||||
import xiaozhi.modules.security.user.SecurityUser;
|
||||
import xiaozhi.modules.sys.service.SysParamsService;
|
||||
import xiaozhi.modules.sys.service.SysUserUtilService;
|
||||
import xiaozhi.modules.device.dto.DeviceManualAddDTO;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@@ -410,4 +411,30 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void manualAddDevice(Long userId, DeviceManualAddDTO dto) {
|
||||
// 检查mac是否已存在
|
||||
QueryWrapper<DeviceEntity> wrapper = new QueryWrapper<>();
|
||||
wrapper.eq("mac_address", dto.getMacAddress());
|
||||
DeviceEntity exist = baseDao.selectOne(wrapper);
|
||||
if (exist != null) {
|
||||
throw new RenException("该Mac地址已存在");
|
||||
}
|
||||
Date now = new Date();
|
||||
DeviceEntity entity = new DeviceEntity();
|
||||
entity.setId(dto.getMacAddress());
|
||||
entity.setUserId(userId);
|
||||
entity.setAgentId(dto.getAgentId());
|
||||
entity.setBoard(dto.getBoard());
|
||||
entity.setAppVersion(dto.getAppVersion());
|
||||
entity.setMacAddress(dto.getMacAddress());
|
||||
entity.setCreateDate(now);
|
||||
entity.setUpdateDate(now);
|
||||
entity.setLastConnectedAt(now);
|
||||
entity.setCreator(userId);
|
||||
entity.setUpdater(userId);
|
||||
entity.setAutoUpdate(1);
|
||||
baseDao.insert(entity);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -218,7 +218,7 @@ public class SysParamsController {
|
||||
if (response.getStatusCode() != HttpStatus.OK) {
|
||||
throw new RenException("MCP接口访问失败,状态码:" + response.getStatusCode());
|
||||
}
|
||||
// 检查响应内容是否包含OTA相关信息
|
||||
// 检查响应内容是否包含mcp相关信息
|
||||
String body = response.getBody();
|
||||
if (body == null || !body.contains("success")) {
|
||||
throw new RenException("MCP接口返回内容格式不正确,可能不是一个真实的MCP接口");
|
||||
|
||||
@@ -137,6 +137,37 @@ public class WebSocketClientManager implements Closeable {
|
||||
return collected;
|
||||
}
|
||||
|
||||
private <T> List<T> listenerCustomWithoutClose(
|
||||
BlockingQueue<T> queue,
|
||||
Predicate<T> predicate)
|
||||
throws InterruptedException, TimeoutException, ExecutionException {
|
||||
List<T> collected = new ArrayList<>();
|
||||
long deadline = System.currentTimeMillis() + maxSessionDurationUnit.toMillis(maxSessionDuration);
|
||||
|
||||
while (true) {
|
||||
if (errorFuture.isDone()) {
|
||||
errorFuture.get();
|
||||
}
|
||||
|
||||
long remaining = deadline - System.currentTimeMillis();
|
||||
if (remaining <= 0) {
|
||||
throw new TimeoutException("等待批量消息超时");
|
||||
}
|
||||
|
||||
T msg = queue.poll(remaining, TimeUnit.MILLISECONDS);
|
||||
if (msg == null) {
|
||||
throw new TimeoutException("等待批量消息超时");
|
||||
}
|
||||
|
||||
collected.add(msg);
|
||||
if (predicate.test(msg)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 不调用 close(),保持连接开放
|
||||
return collected;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步接收多条消息,直到 predicate 为 true 或超时抛异常;
|
||||
*
|
||||
@@ -147,6 +178,17 @@ public class WebSocketClientManager implements Closeable {
|
||||
return listenerCustom(textMessageQueue, predicate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步接收多条消息,直到 predicate 为 true 或超时抛异常;
|
||||
* 不自动关闭连接,适用于需要在同一连接上发送多个消息的场景
|
||||
*
|
||||
* @return 返回监听期间的所有消息列表
|
||||
*/
|
||||
public List<String> listenerWithoutClose(Predicate<String> predicate)
|
||||
throws InterruptedException, TimeoutException, ExecutionException {
|
||||
return listenerCustomWithoutClose(textMessageQueue, predicate);
|
||||
}
|
||||
|
||||
public List<byte[]> listenerBinary(Predicate<byte[]> predicate)
|
||||
throws InterruptedException, TimeoutException, ExecutionException {
|
||||
return listenerCustom(binaryMessageQueue, predicate);
|
||||
|
||||
@@ -26,6 +26,12 @@ public class TimbreDataDTO {
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "参考音频路径")
|
||||
private String referenceAudio;
|
||||
|
||||
@Schema(description = "參考文本")
|
||||
private String referenceText;
|
||||
|
||||
@Schema(description = "排序")
|
||||
@Min(value = 0, message = "{sort.number}")
|
||||
private long sort;
|
||||
|
||||
@@ -34,6 +34,12 @@ public class TimbreEntity {
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "参考音频路径")
|
||||
private String referenceAudio;
|
||||
|
||||
@Schema(description = "參考文本")
|
||||
private String referenceText;
|
||||
|
||||
@Schema(description = "排序")
|
||||
private long sort;
|
||||
|
||||
|
||||
@@ -25,6 +25,12 @@ public class TimbreDetailsVO implements Serializable {
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "参考音频路径")
|
||||
private String referenceAudio;
|
||||
|
||||
@Schema(description = "參考文本")
|
||||
private String referenceText;
|
||||
|
||||
@Schema(description = "排序")
|
||||
private long sort;
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE `ai_tts_voice`
|
||||
ADD COLUMN `reference_audio` VARCHAR(500) DEFAULT NULL COMMENT '参考音频路径' AFTER `remark`,
|
||||
ADD COLUMN `reference_text` VARCHAR(500) DEFAULT NULL COMMENT '参考文本' AFTER `reference_audio`;
|
||||
@@ -0,0 +1,18 @@
|
||||
-- 更新现有的 get_news_from_newsnow 插件配置
|
||||
UPDATE ai_model_provider
|
||||
SET fields = JSON_ARRAY(
|
||||
JSON_OBJECT(
|
||||
'key', 'url',
|
||||
'type', 'string',
|
||||
'label', '接口地址',
|
||||
'default', 'https://newsnow.busiyi.world/api/s?id='
|
||||
),
|
||||
JSON_OBJECT(
|
||||
'key', 'news_sources',
|
||||
'type', 'string',
|
||||
'label', '新闻源配置',
|
||||
'default', '澎湃新闻;百度热搜;财联社'
|
||||
)
|
||||
)
|
||||
WHERE provider_code = 'get_news_from_newsnow'
|
||||
AND model_type = 'Plugin';
|
||||
@@ -205,6 +205,13 @@ databaseChangeLog:
|
||||
- sqlFile:
|
||||
encoding: utf8
|
||||
path: classpath:db/changelog/202506080955.sql
|
||||
- changeSet:
|
||||
id: 202506091720
|
||||
author: shane0411
|
||||
changes:
|
||||
- sqlFile:
|
||||
encoding: utf8
|
||||
path: classpath:db/changelog/202506091720.sql
|
||||
- changeSet:
|
||||
id: 202506161101
|
||||
author: hrz
|
||||
@@ -219,6 +226,13 @@ databaseChangeLog:
|
||||
- sqlFile:
|
||||
encoding: utf8
|
||||
path: classpath:db/changelog/202506191643.sql
|
||||
- changeSet:
|
||||
id: 202506251620
|
||||
author: Tink
|
||||
changes:
|
||||
- sqlFile:
|
||||
encoding: utf8
|
||||
path: classpath:db/changelog/202506251620.sql
|
||||
- changeSet:
|
||||
id: 202506261637
|
||||
author: hrz
|
||||
|
||||
@@ -1 +1 @@
|
||||
redis.call('FLUSHALL')
|
||||
redis.call('FLUSHDB')
|
||||
Reference in New Issue
Block a user