mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-27 01:23:55 +08:00
update:替换词功能
This commit is contained in:
@@ -95,6 +95,14 @@ public class SwaggerConfig {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public GroupedOpenApi correctWordApi() {
|
||||||
|
return GroupedOpenApi.builder()
|
||||||
|
.group("correct-word")
|
||||||
|
.pathsToMatch("/correct-word/**")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public OpenAPI customOpenAPI() {
|
public OpenAPI customOpenAPI() {
|
||||||
return new OpenAPI().info(new Info()
|
return new OpenAPI().info(new Info()
|
||||||
|
|||||||
@@ -256,4 +256,8 @@ public interface ErrorCode {
|
|||||||
int MCP_ACCESS_POINT_ADDRESS_NO_PERMISSION = 10200; // 没有权限查看该智能体的MCP接入点地址
|
int MCP_ACCESS_POINT_ADDRESS_NO_PERMISSION = 10200; // 没有权限查看该智能体的MCP接入点地址
|
||||||
int MCP_ACCESS_POINT_ADDRESS_NOT_CONFIGURED = 10201; // 请联系管理员进入参数管理配置mcp接入点地址
|
int MCP_ACCESS_POINT_ADDRESS_NOT_CONFIGURED = 10201; // 请联系管理员进入参数管理配置mcp接入点地址
|
||||||
int MCP_ACCESS_POINT_TOOLS_LIST_NO_PERMISSION = 10202; // 没有权限查看该智能体的MCP工具列表
|
int MCP_ACCESS_POINT_TOOLS_LIST_NO_PERMISSION = 10202; // 没有权限查看该智能体的MCP工具列表
|
||||||
|
|
||||||
|
// 替换词相关错误码
|
||||||
|
int CORRECT_WORD_FILE_NAME_EXISTS = 10203; // 文件名已存在
|
||||||
|
int FILE_SIZE_OVER_LIMIT = 10204; // 文件大小超过限制
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ import xiaozhi.modules.agent.service.AgentContextProviderService;
|
|||||||
import xiaozhi.modules.agent.service.AgentPluginMappingService;
|
import xiaozhi.modules.agent.service.AgentPluginMappingService;
|
||||||
import xiaozhi.modules.agent.service.AgentService;
|
import xiaozhi.modules.agent.service.AgentService;
|
||||||
import xiaozhi.modules.agent.service.AgentTemplateService;
|
import xiaozhi.modules.agent.service.AgentTemplateService;
|
||||||
|
import xiaozhi.modules.correctword.service.CorrectWordFileService;
|
||||||
import xiaozhi.modules.agent.vo.AgentChatHistoryUserVO;
|
import xiaozhi.modules.agent.vo.AgentChatHistoryUserVO;
|
||||||
import xiaozhi.modules.agent.vo.AgentInfoVO;
|
import xiaozhi.modules.agent.vo.AgentInfoVO;
|
||||||
import xiaozhi.modules.device.entity.DeviceEntity;
|
import xiaozhi.modules.device.entity.DeviceEntity;
|
||||||
@@ -73,6 +74,7 @@ public class AgentController {
|
|||||||
private final AgentChatSummaryService agentChatSummaryService;
|
private final AgentChatSummaryService agentChatSummaryService;
|
||||||
private final RedisUtils redisUtils;
|
private final RedisUtils redisUtils;
|
||||||
private final AgentTagService agentTagService;
|
private final AgentTagService agentTagService;
|
||||||
|
private final CorrectWordFileService correctWordFileService;
|
||||||
|
|
||||||
@GetMapping("/list")
|
@GetMapping("/list")
|
||||||
@Operation(summary = "获取用户智能体列表")
|
@Operation(summary = "获取用户智能体列表")
|
||||||
@@ -177,6 +179,8 @@ public class AgentController {
|
|||||||
agentPluginMappingService.deleteByAgentId(id);
|
agentPluginMappingService.deleteByAgentId(id);
|
||||||
// 删除关联的上下文源配置
|
// 删除关联的上下文源配置
|
||||||
agentContextProviderService.deleteByAgentId(id);
|
agentContextProviderService.deleteByAgentId(id);
|
||||||
|
// 删除关联的替换词文件关联记录
|
||||||
|
correctWordFileService.deleteMappingsByAgentId(id);
|
||||||
// 再删除智能体
|
// 再删除智能体
|
||||||
agentService.deleteById(id);
|
agentService.deleteById(id);
|
||||||
return new Result<>();
|
return new Result<>();
|
||||||
|
|||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
package xiaozhi.modules.agent.dao;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import xiaozhi.common.dao.BaseDao;
|
||||||
|
import xiaozhi.modules.agent.entity.AgentCorrectWordMappingEntity;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface AgentCorrectWordMappingDao extends BaseDao<AgentCorrectWordMappingEntity> {
|
||||||
|
|
||||||
|
int deleteByAgentId(@Param("agentId") String agentId);
|
||||||
|
|
||||||
|
int deleteByFileId(@Param("fileId") String fileId);
|
||||||
|
|
||||||
|
int batchInsertMapping(@Param("list") List<AgentCorrectWordMappingEntity> mappings);
|
||||||
|
|
||||||
|
List<AgentCorrectWordMappingEntity> selectByAgentId(@Param("agentId") String agentId);
|
||||||
|
}
|
||||||
@@ -88,6 +88,9 @@ public class AgentUpdateDTO implements Serializable {
|
|||||||
@Schema(description = "上下文源配置", nullable = true)
|
@Schema(description = "上下文源配置", nullable = true)
|
||||||
private List<ContextProviderDTO> contextProviders;
|
private List<ContextProviderDTO> contextProviders;
|
||||||
|
|
||||||
|
@Schema(description = "替换词文件ID列表", nullable = true)
|
||||||
|
private List<String> correctWordFileIds;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@Schema(description = "插件函数信息")
|
@Schema(description = "插件函数信息")
|
||||||
public static class FunctionInfo implements Serializable {
|
public static class FunctionInfo implements Serializable {
|
||||||
|
|||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
package xiaozhi.modules.agent.entity;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@TableName("ai_agent_correct_word_mapping")
|
||||||
|
@Schema(description = "智能体替换词文件关联")
|
||||||
|
public class AgentCorrectWordMappingEntity {
|
||||||
|
|
||||||
|
@TableId(type = IdType.ASSIGN_UUID)
|
||||||
|
@Schema(description = "主键")
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Schema(description = "智能体ID")
|
||||||
|
private String agentId;
|
||||||
|
|
||||||
|
@Schema(description = "替换词文件ID")
|
||||||
|
private String fileId;
|
||||||
|
|
||||||
|
@Schema(description = "创建者")
|
||||||
|
private Long creator;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
private Date createdAt;
|
||||||
|
|
||||||
|
@Schema(description = "更新者")
|
||||||
|
private Long updater;
|
||||||
|
|
||||||
|
@Schema(description = "更新时间")
|
||||||
|
private Date updatedAt;
|
||||||
|
}
|
||||||
+11
@@ -48,6 +48,7 @@ import xiaozhi.modules.agent.service.AgentService;
|
|||||||
import xiaozhi.modules.agent.service.AgentTagService;
|
import xiaozhi.modules.agent.service.AgentTagService;
|
||||||
import xiaozhi.modules.agent.service.AgentTemplateService;
|
import xiaozhi.modules.agent.service.AgentTemplateService;
|
||||||
import xiaozhi.modules.agent.vo.AgentInfoVO;
|
import xiaozhi.modules.agent.vo.AgentInfoVO;
|
||||||
|
import xiaozhi.modules.correctword.service.CorrectWordFileService;
|
||||||
import xiaozhi.modules.device.entity.DeviceEntity;
|
import xiaozhi.modules.device.entity.DeviceEntity;
|
||||||
import xiaozhi.modules.device.service.DeviceService;
|
import xiaozhi.modules.device.service.DeviceService;
|
||||||
import xiaozhi.modules.model.dto.ModelProviderDTO;
|
import xiaozhi.modules.model.dto.ModelProviderDTO;
|
||||||
@@ -74,6 +75,7 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
|
|||||||
private final ModelProviderService modelProviderService;
|
private final ModelProviderService modelProviderService;
|
||||||
private final AgentContextProviderService agentContextProviderService;
|
private final AgentContextProviderService agentContextProviderService;
|
||||||
private final AgentTagService agentTagService;
|
private final AgentTagService agentTagService;
|
||||||
|
private final CorrectWordFileService correctWordFileService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageData<AgentEntity> adminAgentList(Map<String, Object> params) {
|
public PageData<AgentEntity> adminAgentList(Map<String, Object> params) {
|
||||||
@@ -104,6 +106,10 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
|
|||||||
agent.setContextProviders(contextProviderEntity.getContextProviders());
|
agent.setContextProviders(contextProviderEntity.getContextProviders());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询替换词文件ID列表
|
||||||
|
List<String> correctWordFileIds = correctWordFileService.getAgentCorrectWordFileIds(id);
|
||||||
|
agent.setCorrectWordFileIds(correctWordFileIds);
|
||||||
|
|
||||||
// 无需额外查询插件列表,已通过SQL查询出来
|
// 无需额外查询插件列表,已通过SQL查询出来
|
||||||
return agent;
|
return agent;
|
||||||
}
|
}
|
||||||
@@ -427,6 +433,11 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
|
|||||||
agentContextProviderService.saveOrUpdateByAgentId(contextEntity);
|
agentContextProviderService.saveOrUpdateByAgentId(contextEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新替换词文件关联
|
||||||
|
if (dto.getCorrectWordFileIds() != null) {
|
||||||
|
correctWordFileService.saveAgentCorrectWords(agentId, dto.getCorrectWordFileIds());
|
||||||
|
}
|
||||||
|
|
||||||
boolean b = validateLLMIntentParams(dto.getLlmModelId(), dto.getIntentModelId());
|
boolean b = validateLLMIntentParams(dto.getLlmModelId(), dto.getIntentModelId());
|
||||||
if (!b) {
|
if (!b) {
|
||||||
throw new RenException(ErrorCode.LLM_INTENT_PARAMS_MISMATCH);
|
throw new RenException(ErrorCode.LLM_INTENT_PARAMS_MISMATCH);
|
||||||
|
|||||||
@@ -25,4 +25,7 @@ public class AgentInfoVO extends AgentEntity
|
|||||||
|
|
||||||
@Schema(description = "上下文源配置")
|
@Schema(description = "上下文源配置")
|
||||||
private List<ContextProviderDTO> contextProviders;
|
private List<ContextProviderDTO> contextProviders;
|
||||||
|
|
||||||
|
@Schema(description = "替换词文件ID列表")
|
||||||
|
private List<String> correctWordFileIds;
|
||||||
}
|
}
|
||||||
|
|||||||
+11
@@ -1,5 +1,7 @@
|
|||||||
package xiaozhi.modules.config.controller;
|
package xiaozhi.modules.config.controller;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
@@ -12,6 +14,7 @@ import lombok.AllArgsConstructor;
|
|||||||
import xiaozhi.common.utils.Result;
|
import xiaozhi.common.utils.Result;
|
||||||
import xiaozhi.common.validator.ValidatorUtils;
|
import xiaozhi.common.validator.ValidatorUtils;
|
||||||
import xiaozhi.modules.config.dto.AgentModelsDTO;
|
import xiaozhi.modules.config.dto.AgentModelsDTO;
|
||||||
|
import xiaozhi.modules.config.dto.CorrectWordsDTO;
|
||||||
import xiaozhi.modules.config.service.ConfigService;
|
import xiaozhi.modules.config.service.ConfigService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,4 +44,12 @@ public class ConfigController {
|
|||||||
Object models = configService.getAgentModels(dto.getMacAddress(), dto.getSelectedModule());
|
Object models = configService.getAgentModels(dto.getMacAddress(), dto.getSelectedModule());
|
||||||
return new Result<Object>().ok(models);
|
return new Result<Object>().ok(models);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("correct-words")
|
||||||
|
@Operation(summary = "获取智能体替换词")
|
||||||
|
public Result<Object> getCorrectWords(@Valid @RequestBody CorrectWordsDTO dto) {
|
||||||
|
ValidatorUtils.validateEntity(dto);
|
||||||
|
List<String> list = configService.getCorrectWords(dto.getMacAddress());
|
||||||
|
return new Result<Object>().ok(list);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package xiaozhi.modules.config.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "获取智能体替换词DTO")
|
||||||
|
public class CorrectWordsDTO {
|
||||||
|
|
||||||
|
@NotBlank(message = "设备MAC地址不能为空")
|
||||||
|
@Schema(description = "设备MAC地址")
|
||||||
|
private String macAddress;
|
||||||
|
}
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
package xiaozhi.modules.config.service;
|
package xiaozhi.modules.config.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public interface ConfigService {
|
public interface ConfigService {
|
||||||
/**
|
/**
|
||||||
* 获取服务器配置
|
* 获取服务器配置
|
||||||
*
|
*
|
||||||
* @param isCache 是否缓存
|
* @param isCache 是否缓存
|
||||||
* @return 配置信息
|
* @return 配置信息
|
||||||
*/
|
*/
|
||||||
@@ -13,10 +14,18 @@ public interface ConfigService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取智能体模型配置
|
* 获取智能体模型配置
|
||||||
*
|
*
|
||||||
* @param macAddress MAC地址
|
* @param macAddress MAC地址
|
||||||
* @param selectedModule 客户端已实例化的模型
|
* @param selectedModule 客户端已实例化的模型
|
||||||
* @return 模型配置信息
|
* @return 模型配置信息
|
||||||
*/
|
*/
|
||||||
Map<String, Object> getAgentModels(String macAddress, Map<String, String> selectedModule);
|
Map<String, Object> getAgentModels(String macAddress, Map<String, String> selectedModule);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取智能体替换词
|
||||||
|
*
|
||||||
|
* @param macAddress 设备MAC地址
|
||||||
|
* @return 替换词列表,格式如 ["模板1|模板01", "模板2|模板02"]
|
||||||
|
*/
|
||||||
|
List<String> getCorrectWords(String macAddress);
|
||||||
}
|
}
|
||||||
+17
@@ -1,10 +1,12 @@
|
|||||||
package xiaozhi.modules.config.service.impl;
|
package xiaozhi.modules.config.service.impl;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -30,7 +32,9 @@ import xiaozhi.modules.agent.service.AgentMcpAccessPointService;
|
|||||||
import xiaozhi.modules.agent.service.AgentPluginMappingService;
|
import xiaozhi.modules.agent.service.AgentPluginMappingService;
|
||||||
import xiaozhi.modules.agent.service.AgentService;
|
import xiaozhi.modules.agent.service.AgentService;
|
||||||
import xiaozhi.modules.agent.service.AgentTemplateService;
|
import xiaozhi.modules.agent.service.AgentTemplateService;
|
||||||
|
import xiaozhi.modules.correctword.service.CorrectWordFileService;
|
||||||
import xiaozhi.modules.agent.vo.AgentVoicePrintVO;
|
import xiaozhi.modules.agent.vo.AgentVoicePrintVO;
|
||||||
|
import xiaozhi.modules.correctword.vo.CorrectWordSimpleVO;
|
||||||
import xiaozhi.modules.config.service.ConfigService;
|
import xiaozhi.modules.config.service.ConfigService;
|
||||||
import xiaozhi.modules.device.entity.DeviceEntity;
|
import xiaozhi.modules.device.entity.DeviceEntity;
|
||||||
import xiaozhi.modules.device.service.DeviceService;
|
import xiaozhi.modules.device.service.DeviceService;
|
||||||
@@ -58,6 +62,7 @@ public class ConfigServiceImpl implements ConfigService {
|
|||||||
private final AgentContextProviderService agentContextProviderService;
|
private final AgentContextProviderService agentContextProviderService;
|
||||||
private final VoiceCloneService cloneVoiceService;
|
private final VoiceCloneService cloneVoiceService;
|
||||||
private final AgentVoicePrintDao agentVoicePrintDao;
|
private final AgentVoicePrintDao agentVoicePrintDao;
|
||||||
|
private final CorrectWordFileService correctWordFileService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getConfig(Boolean isCache) {
|
public Object getConfig(Boolean isCache) {
|
||||||
@@ -242,6 +247,18 @@ public class ConfigServiceImpl implements ConfigService {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getCorrectWords(String macAddress) {
|
||||||
|
DeviceEntity device = deviceService.getDeviceByMacAddress(macAddress);
|
||||||
|
if (device == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
List<CorrectWordSimpleVO> items = correctWordFileService.getAllItemsByAgentId(device.getAgentId());
|
||||||
|
return items.stream()
|
||||||
|
.map(item -> item.getSourceWord() + "|" + item.getTargetWord())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建配置信息
|
* 构建配置信息
|
||||||
*
|
*
|
||||||
|
|||||||
+116
@@ -0,0 +1,116 @@
|
|||||||
|
package xiaozhi.modules.correctword.controller;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
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.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameters;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import xiaozhi.common.constant.Constant;
|
||||||
|
import xiaozhi.common.page.PageData;
|
||||||
|
import xiaozhi.common.utils.Result;
|
||||||
|
import xiaozhi.modules.correctword.dto.CorrectWordFileCreateDTO;
|
||||||
|
import xiaozhi.modules.correctword.service.CorrectWordFileService;
|
||||||
|
import xiaozhi.modules.correctword.vo.CorrectWordFileVO;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/correct-word")
|
||||||
|
@Tag(name = "替换词管理")
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class CorrectWordController {
|
||||||
|
|
||||||
|
private final CorrectWordFileService correctWordFileService;
|
||||||
|
|
||||||
|
@PostMapping("/file")
|
||||||
|
@Operation(summary = "创建替换词文件")
|
||||||
|
@RequiresPermissions("sys:role:normal")
|
||||||
|
public Result<CorrectWordFileVO> createFile(@Valid @RequestBody CorrectWordFileCreateDTO dto) {
|
||||||
|
CorrectWordFileVO vo = correctWordFileService.createFile(dto);
|
||||||
|
return new Result<CorrectWordFileVO>().ok(vo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/file/{fileId}")
|
||||||
|
@Operation(summary = "修改替换词文件")
|
||||||
|
@RequiresPermissions("sys:role:normal")
|
||||||
|
public Result<Void> updateFile(@PathVariable String fileId, @Valid @RequestBody CorrectWordFileCreateDTO dto) {
|
||||||
|
correctWordFileService.updateFile(fileId, dto);
|
||||||
|
return new Result<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/file/list")
|
||||||
|
@Operation(summary = "分页获取当前用户替换词文件列表")
|
||||||
|
@RequiresPermissions("sys:role:normal")
|
||||||
|
@Parameters({
|
||||||
|
@Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true),
|
||||||
|
@Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true),
|
||||||
|
})
|
||||||
|
public Result<PageData<CorrectWordFileVO>> listFiles(
|
||||||
|
@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
|
||||||
|
PageData<CorrectWordFileVO> page = correctWordFileService.listFiles(params);
|
||||||
|
return new Result<PageData<CorrectWordFileVO>>().ok(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/file/select")
|
||||||
|
@Operation(summary = "智能体获取当前用户替换词文件列表")
|
||||||
|
@RequiresPermissions("sys:role:normal")
|
||||||
|
public Result<List<CorrectWordFileVO>> listAllFiles() {
|
||||||
|
List<CorrectWordFileVO> list = correctWordFileService.listAllFiles();
|
||||||
|
return new Result<List<CorrectWordFileVO>>().ok(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/file/download/{fileId}")
|
||||||
|
@Operation(summary = "下载替换词文件")
|
||||||
|
@RequiresPermissions("sys:role:normal")
|
||||||
|
public ResponseEntity<byte[]> downloadFile(@PathVariable String fileId) {
|
||||||
|
CorrectWordFileVO vo = correctWordFileService.getFileContent(fileId);
|
||||||
|
if (vo == null || vo.getContent() == null || vo.getContent().isEmpty()) {
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
byte[] bytes = String.join("\n", vo.getContent()).getBytes(StandardCharsets.UTF_8);
|
||||||
|
String encodedFileName = URLEncoder.encode(vo.getFileName(), StandardCharsets.UTF_8).replace("+", "%20");
|
||||||
|
String asciiFileName = vo.getFileName().replaceAll("[^\\x00-\\x7F]", "_");
|
||||||
|
return ResponseEntity.ok()
|
||||||
|
.header(HttpHeaders.CONTENT_DISPOSITION,
|
||||||
|
"attachment; filename=\"" + asciiFileName + "\"; filename*=UTF-8''" + encodedFileName)
|
||||||
|
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
|
.body(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/file/{fileId}")
|
||||||
|
@Operation(summary = "删除替换词文件")
|
||||||
|
@RequiresPermissions("sys:role:normal")
|
||||||
|
public Result<Void> deleteFile(@PathVariable String fileId) {
|
||||||
|
correctWordFileService.deleteFile(fileId);
|
||||||
|
return new Result<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/file/batch-delete")
|
||||||
|
@Operation(summary = "批量删除替换词文件")
|
||||||
|
@RequiresPermissions("sys:role:normal")
|
||||||
|
public Result<Void> batchDeleteFiles(@RequestBody List<String> fileIds) {
|
||||||
|
if (fileIds == null || fileIds.isEmpty()) {
|
||||||
|
return new Result<>();
|
||||||
|
}
|
||||||
|
correctWordFileService.batchDeleteFiles(fileIds);
|
||||||
|
return new Result<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
package xiaozhi.modules.correctword.dao;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
import xiaozhi.common.dao.BaseDao;
|
||||||
|
import xiaozhi.modules.correctword.entity.CorrectWordFileEntity;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface CorrectWordFileDao extends BaseDao<CorrectWordFileEntity> {
|
||||||
|
}
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
package xiaozhi.modules.correctword.dao;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import xiaozhi.common.dao.BaseDao;
|
||||||
|
import xiaozhi.modules.correctword.entity.CorrectWordItemEntity;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface CorrectWordItemDao extends BaseDao<CorrectWordItemEntity> {
|
||||||
|
|
||||||
|
int batchInsert(@Param("list") List<CorrectWordItemEntity> items);
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
package xiaozhi.modules.correctword.dto;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "创建替换词文件DTO")
|
||||||
|
public class CorrectWordFileCreateDTO {
|
||||||
|
|
||||||
|
@NotBlank(message = "文件名不能为空")
|
||||||
|
@Schema(description = "文件名")
|
||||||
|
private String fileName;
|
||||||
|
|
||||||
|
@NotEmpty(message = "替换词内容不能为空")
|
||||||
|
@Schema(description = "替换词内容,每条格式:原词|替换词")
|
||||||
|
private List<String> content;
|
||||||
|
|
||||||
|
@Schema(description = "文件大小(字节),不能超过1MB")
|
||||||
|
private Long fileSize;
|
||||||
|
}
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
package xiaozhi.modules.correctword.entity;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@TableName("ai_agent_correct_word_file")
|
||||||
|
@Schema(description = "智能体替换词文件")
|
||||||
|
public class CorrectWordFileEntity {
|
||||||
|
|
||||||
|
@TableId(type = IdType.ASSIGN_UUID)
|
||||||
|
@Schema(description = "替换词文件ID")
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Schema(description = "原始文件名")
|
||||||
|
private String fileName;
|
||||||
|
|
||||||
|
@Schema(description = "替换词数量")
|
||||||
|
private Integer wordCount;
|
||||||
|
|
||||||
|
@Schema(description = "文件原始内容(用于下载)")
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@Schema(description = "创建者")
|
||||||
|
private Long creator;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
private Date createdAt;
|
||||||
|
|
||||||
|
@Schema(description = "更新者")
|
||||||
|
private Long updater;
|
||||||
|
|
||||||
|
@Schema(description = "更新时间")
|
||||||
|
private Date updatedAt;
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
package xiaozhi.modules.correctword.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@TableName("ai_agent_correct_word_item")
|
||||||
|
@Schema(description = "替换词词条")
|
||||||
|
public class CorrectWordItemEntity {
|
||||||
|
|
||||||
|
@TableId(type = IdType.ASSIGN_UUID)
|
||||||
|
@Schema(description = "词条ID")
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Schema(description = "所属文件ID")
|
||||||
|
private String fileId;
|
||||||
|
|
||||||
|
@Schema(description = "原词")
|
||||||
|
private String sourceWord;
|
||||||
|
|
||||||
|
@Schema(description = "替换词")
|
||||||
|
private String targetWord;
|
||||||
|
}
|
||||||
+96
@@ -0,0 +1,96 @@
|
|||||||
|
package xiaozhi.modules.correctword.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import xiaozhi.common.page.PageData;
|
||||||
|
import xiaozhi.modules.correctword.dto.CorrectWordFileCreateDTO;
|
||||||
|
import xiaozhi.modules.correctword.vo.CorrectWordFileVO;
|
||||||
|
import xiaozhi.modules.correctword.vo.CorrectWordSimpleVO;
|
||||||
|
|
||||||
|
public interface CorrectWordFileService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建替换词文件
|
||||||
|
*
|
||||||
|
* @param dto 创建参数
|
||||||
|
* @return 文件VO
|
||||||
|
*/
|
||||||
|
CorrectWordFileVO createFile(CorrectWordFileCreateDTO dto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改替换词文件(全量替换词条)
|
||||||
|
*
|
||||||
|
* @param fileId 文件ID
|
||||||
|
* @param dto 修改参数
|
||||||
|
*/
|
||||||
|
void updateFile(String fileId, CorrectWordFileCreateDTO dto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前用户的替换词文件列表
|
||||||
|
*
|
||||||
|
* @param params 分页参数
|
||||||
|
* @return 分页数据
|
||||||
|
*/
|
||||||
|
PageData<CorrectWordFileVO> listFiles(Map<String, Object> params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前用户的替换词文件列表(不分页,用于下拉选择)
|
||||||
|
*
|
||||||
|
* @return 文件列表
|
||||||
|
*/
|
||||||
|
List<CorrectWordFileVO> listAllFiles();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件原始内容(用于下载)
|
||||||
|
*
|
||||||
|
* @param fileId 文件ID
|
||||||
|
* @return 文件实体
|
||||||
|
*/
|
||||||
|
CorrectWordFileVO getFileContent(String fileId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除替换词文件及其所有词条和关联记录
|
||||||
|
*
|
||||||
|
* @param fileId 文件ID
|
||||||
|
*/
|
||||||
|
void deleteFile(String fileId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除智能体关联的替换词文件关联记录(不删文件本身)
|
||||||
|
*
|
||||||
|
* @param agentId 智能体ID
|
||||||
|
*/
|
||||||
|
void deleteMappingsByAgentId(String agentId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取智能体的所有替换词条(精简版,供设备端使用)
|
||||||
|
*
|
||||||
|
* @param agentId 智能体ID
|
||||||
|
* @return 替换词列表
|
||||||
|
*/
|
||||||
|
List<CorrectWordSimpleVO> getAllItemsByAgentId(String agentId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取智能体关联的替换词文件ID列表
|
||||||
|
*
|
||||||
|
* @param agentId 智能体ID
|
||||||
|
* @return 文件ID列表
|
||||||
|
*/
|
||||||
|
List<String> getAgentCorrectWordFileIds(String agentId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存智能体关联的替换词文件(全量替换)
|
||||||
|
*
|
||||||
|
* @param agentId 智能体ID
|
||||||
|
* @param fileIds 文件ID列表
|
||||||
|
*/
|
||||||
|
void saveAgentCorrectWords(String agentId, List<String> fileIds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除替换词文件
|
||||||
|
*
|
||||||
|
* @param fileIds 文件ID列表
|
||||||
|
*/
|
||||||
|
void batchDeleteFiles(List<String> fileIds);
|
||||||
|
}
|
||||||
+296
@@ -0,0 +1,296 @@
|
|||||||
|
package xiaozhi.modules.correctword.service.impl;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import xiaozhi.common.exception.ErrorCode;
|
||||||
|
import xiaozhi.common.exception.RenException;
|
||||||
|
import xiaozhi.common.page.PageData;
|
||||||
|
import xiaozhi.common.service.impl.BaseServiceImpl;
|
||||||
|
import xiaozhi.common.utils.ConvertUtils;
|
||||||
|
import xiaozhi.modules.agent.dao.AgentCorrectWordMappingDao;
|
||||||
|
import xiaozhi.modules.correctword.dao.CorrectWordFileDao;
|
||||||
|
import xiaozhi.modules.correctword.dao.CorrectWordItemDao;
|
||||||
|
import xiaozhi.modules.correctword.dto.CorrectWordFileCreateDTO;
|
||||||
|
import xiaozhi.modules.agent.entity.AgentCorrectWordMappingEntity;
|
||||||
|
import xiaozhi.modules.correctword.entity.CorrectWordFileEntity;
|
||||||
|
import xiaozhi.modules.correctword.entity.CorrectWordItemEntity;
|
||||||
|
import xiaozhi.modules.correctword.service.CorrectWordFileService;
|
||||||
|
import xiaozhi.modules.correctword.vo.CorrectWordFileVO;
|
||||||
|
import xiaozhi.modules.correctword.vo.CorrectWordSimpleVO;
|
||||||
|
import xiaozhi.modules.security.user.SecurityUser;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class CorrectWordFileServiceImpl extends BaseServiceImpl<CorrectWordFileDao, CorrectWordFileEntity>
|
||||||
|
implements CorrectWordFileService {
|
||||||
|
|
||||||
|
private final CorrectWordFileDao correctWordFileDao;
|
||||||
|
private final CorrectWordItemDao correctWordItemDao;
|
||||||
|
private final AgentCorrectWordMappingDao agentCorrectWordMappingDao;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public CorrectWordFileVO createFile(CorrectWordFileCreateDTO dto) {
|
||||||
|
// 校验文件大小不能超过1MB
|
||||||
|
if (dto.getFileSize() != null && dto.getFileSize() > 1024 * 1024) {
|
||||||
|
throw new RenException(ErrorCode.FILE_SIZE_OVER_LIMIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验文件名是否重复
|
||||||
|
Long userId = SecurityUser.getUserId();
|
||||||
|
LambdaQueryWrapper<CorrectWordFileEntity> nameWrapper = new LambdaQueryWrapper<>();
|
||||||
|
nameWrapper.eq(CorrectWordFileEntity::getCreator, userId)
|
||||||
|
.eq(CorrectWordFileEntity::getFileName, dto.getFileName());
|
||||||
|
if (correctWordFileDao.selectCount(nameWrapper) > 0) {
|
||||||
|
throw new RenException(ErrorCode.CORRECT_WORD_FILE_NAME_EXISTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<CorrectWordItemEntity> items = parseContent(dto.getContent());
|
||||||
|
|
||||||
|
// 保存文件记录
|
||||||
|
CorrectWordFileEntity fileEntity = new CorrectWordFileEntity();
|
||||||
|
fileEntity.setFileName(dto.getFileName());
|
||||||
|
fileEntity.setWordCount(items.size());
|
||||||
|
fileEntity.setContent(String.join("\n", dto.getContent()));
|
||||||
|
fileEntity.setCreator(SecurityUser.getUserId());
|
||||||
|
fileEntity.setCreatedAt(new Date());
|
||||||
|
correctWordFileDao.insert(fileEntity);
|
||||||
|
|
||||||
|
// 设置fileId并批量保存词条
|
||||||
|
String fileId = fileEntity.getId();
|
||||||
|
for (CorrectWordItemEntity item : items) {
|
||||||
|
item.setFileId(fileId);
|
||||||
|
}
|
||||||
|
if (!items.isEmpty()) {
|
||||||
|
correctWordItemDao.batchInsert(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
return toVO(fileEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void updateFile(String fileId, CorrectWordFileCreateDTO dto) {
|
||||||
|
CorrectWordFileEntity fileEntity = correctWordFileDao.selectById(fileId);
|
||||||
|
if (fileEntity == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验文件名是否重复(排除自身)
|
||||||
|
Long userId = SecurityUser.getUserId();
|
||||||
|
LambdaQueryWrapper<CorrectWordFileEntity> nameWrapper = new LambdaQueryWrapper<>();
|
||||||
|
nameWrapper.eq(CorrectWordFileEntity::getCreator, userId)
|
||||||
|
.eq(CorrectWordFileEntity::getFileName, dto.getFileName())
|
||||||
|
.ne(CorrectWordFileEntity::getId, fileId);
|
||||||
|
if (correctWordFileDao.selectCount(nameWrapper) > 0) {
|
||||||
|
throw new RenException("文件名已存在:" + dto.getFileName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先删除旧词条
|
||||||
|
LambdaQueryWrapper<CorrectWordItemEntity> deleteWrapper = new LambdaQueryWrapper<>();
|
||||||
|
deleteWrapper.eq(CorrectWordItemEntity::getFileId, fileId);
|
||||||
|
correctWordItemDao.delete(deleteWrapper);
|
||||||
|
|
||||||
|
// 解析新词条并批量保存
|
||||||
|
List<CorrectWordItemEntity> items = parseContent(dto.getContent());
|
||||||
|
if (!items.isEmpty()) {
|
||||||
|
for (CorrectWordItemEntity item : items) {
|
||||||
|
item.setFileId(fileId);
|
||||||
|
}
|
||||||
|
correctWordItemDao.batchInsert(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新文件记录
|
||||||
|
fileEntity.setFileName(dto.getFileName());
|
||||||
|
fileEntity.setWordCount(items.size());
|
||||||
|
fileEntity.setContent(String.join("\n", dto.getContent()));
|
||||||
|
fileEntity.setUpdater(SecurityUser.getUserId());
|
||||||
|
fileEntity.setUpdatedAt(new Date());
|
||||||
|
correctWordFileDao.updateById(fileEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PageData<CorrectWordFileVO> listFiles(Map<String, Object> params) {
|
||||||
|
Long userId = SecurityUser.getUserId();
|
||||||
|
IPage<CorrectWordFileEntity> page = getPage(params, "created_at", false);
|
||||||
|
LambdaQueryWrapper<CorrectWordFileEntity> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(CorrectWordFileEntity::getCreator, userId)
|
||||||
|
.orderByDesc(CorrectWordFileEntity::getCreatedAt);
|
||||||
|
correctWordFileDao.selectPage(page, wrapper);
|
||||||
|
List<CorrectWordFileVO> voList = toVOList(page.getRecords());
|
||||||
|
return new PageData<>(voList, page.getTotal());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CorrectWordFileVO> listAllFiles() {
|
||||||
|
Long userId = SecurityUser.getUserId();
|
||||||
|
LambdaQueryWrapper<CorrectWordFileEntity> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(CorrectWordFileEntity::getCreator, userId)
|
||||||
|
.orderByDesc(CorrectWordFileEntity::getCreatedAt);
|
||||||
|
List<CorrectWordFileEntity> entities = correctWordFileDao.selectList(wrapper);
|
||||||
|
return toVOList(entities);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CorrectWordFileVO getFileContent(String fileId) {
|
||||||
|
CorrectWordFileEntity entity = correctWordFileDao.selectById(fileId);
|
||||||
|
return toVO(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void deleteFile(String fileId) {
|
||||||
|
if (fileId == null || fileId.trim().isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 先删除关联表记录
|
||||||
|
agentCorrectWordMappingDao.deleteByFileId(fileId);
|
||||||
|
// 删除词条
|
||||||
|
LambdaQueryWrapper<CorrectWordItemEntity> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(CorrectWordItemEntity::getFileId, fileId);
|
||||||
|
correctWordItemDao.delete(wrapper);
|
||||||
|
// 删除文件
|
||||||
|
correctWordFileDao.deleteById(fileId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void deleteMappingsByAgentId(String agentId) {
|
||||||
|
agentCorrectWordMappingDao.deleteByAgentId(agentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CorrectWordSimpleVO> getAllItemsByAgentId(String agentId) {
|
||||||
|
// 通过关联表获取文件ID列表
|
||||||
|
List<AgentCorrectWordMappingEntity> mappings = agentCorrectWordMappingDao.selectByAgentId(agentId);
|
||||||
|
if (mappings == null || mappings.isEmpty()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
List<String> fileIds = mappings.stream()
|
||||||
|
.map(AgentCorrectWordMappingEntity::getFileId)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// 根据文件ID列表查询词条
|
||||||
|
LambdaQueryWrapper<CorrectWordItemEntity> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.in(CorrectWordItemEntity::getFileId, fileIds);
|
||||||
|
List<CorrectWordItemEntity> entities = correctWordItemDao.selectList(wrapper);
|
||||||
|
return ConvertUtils.sourceToTarget(entities, CorrectWordSimpleVO.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getAgentCorrectWordFileIds(String agentId) {
|
||||||
|
List<AgentCorrectWordMappingEntity> mappings = agentCorrectWordMappingDao.selectByAgentId(agentId);
|
||||||
|
if (mappings == null || mappings.isEmpty()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return mappings.stream()
|
||||||
|
.map(AgentCorrectWordMappingEntity::getFileId)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void saveAgentCorrectWords(String agentId, List<String> fileIds) {
|
||||||
|
// 先删除旧的关联记录
|
||||||
|
agentCorrectWordMappingDao.deleteByAgentId(agentId);
|
||||||
|
|
||||||
|
if (fileIds == null || fileIds.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量插入新的关联记录
|
||||||
|
Long userId = SecurityUser.getUserId();
|
||||||
|
Date now = new Date();
|
||||||
|
List<AgentCorrectWordMappingEntity> mappings = new ArrayList<>();
|
||||||
|
for (String fileId : fileIds) {
|
||||||
|
AgentCorrectWordMappingEntity mapping = new AgentCorrectWordMappingEntity();
|
||||||
|
mapping.setAgentId(agentId);
|
||||||
|
mapping.setFileId(fileId);
|
||||||
|
mapping.setCreator(userId);
|
||||||
|
mapping.setCreatedAt(now);
|
||||||
|
mapping.setUpdater(userId);
|
||||||
|
mapping.setUpdatedAt(now);
|
||||||
|
mappings.add(mapping);
|
||||||
|
}
|
||||||
|
agentCorrectWordMappingDao.batchInsertMapping(mappings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void batchDeleteFiles(List<String> fileIds) {
|
||||||
|
if (fileIds == null || fileIds.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (String fileId : fileIds) {
|
||||||
|
if (fileId == null || fileId.trim().isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
deleteFile(fileId.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析替换词内容,每条格式:原词|替换词
|
||||||
|
*/
|
||||||
|
private List<CorrectWordItemEntity> parseContent(List<String> lines) {
|
||||||
|
List<CorrectWordItemEntity> items = new ArrayList<>();
|
||||||
|
if (lines == null || lines.isEmpty()) {
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
for (String line : lines) {
|
||||||
|
line = line.trim();
|
||||||
|
if (line.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int idx = line.indexOf('|');
|
||||||
|
if (idx <= 0 || idx >= line.length() - 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String sourceWord = line.substring(0, idx).trim();
|
||||||
|
String targetWord = line.substring(idx + 1).trim();
|
||||||
|
if (sourceWord.isEmpty() || targetWord.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
CorrectWordItemEntity item = new CorrectWordItemEntity();
|
||||||
|
item.setSourceWord(sourceWord);
|
||||||
|
item.setTargetWord(targetWord);
|
||||||
|
items.add(item);
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CorrectWordFileVO toVO(CorrectWordFileEntity entity) {
|
||||||
|
if (entity == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
CorrectWordFileVO vo = new CorrectWordFileVO();
|
||||||
|
vo.setId(entity.getId());
|
||||||
|
vo.setFileName(entity.getFileName());
|
||||||
|
vo.setWordCount(entity.getWordCount());
|
||||||
|
vo.setContent(entity.getContent() != null
|
||||||
|
? Arrays.asList(entity.getContent().split("\n"))
|
||||||
|
: new ArrayList<>());
|
||||||
|
vo.setCreatedAt(entity.getCreatedAt());
|
||||||
|
vo.setUpdatedAt(entity.getUpdatedAt());
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<CorrectWordFileVO> toVOList(List<CorrectWordFileEntity> entities) {
|
||||||
|
if (entities == null || entities.isEmpty()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return entities.stream().map(this::toVO).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package xiaozhi.modules.correctword.vo;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "替换词文件列表VO")
|
||||||
|
public class CorrectWordFileVO {
|
||||||
|
|
||||||
|
@Schema(description = "替换词文件ID")
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Schema(description = "原始文件名")
|
||||||
|
private String fileName;
|
||||||
|
|
||||||
|
@Schema(description = "替换词数量")
|
||||||
|
private Integer wordCount;
|
||||||
|
|
||||||
|
@Schema(description = "替换词内容,每行一条")
|
||||||
|
private List<String> content;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
private Date createdAt;
|
||||||
|
|
||||||
|
@Schema(description = "更新时间")
|
||||||
|
private Date updatedAt;
|
||||||
|
}
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
package xiaozhi.modules.correctword.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "替换词精简VO(设备端使用)")
|
||||||
|
public class CorrectWordSimpleVO {
|
||||||
|
|
||||||
|
@Schema(description = "原词")
|
||||||
|
private String sourceWord;
|
||||||
|
|
||||||
|
@Schema(description = "替换词")
|
||||||
|
private String targetWord;
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
-- 智能体替换词文件表
|
||||||
|
CREATE TABLE IF NOT EXISTS `ai_agent_correct_word_file` (
|
||||||
|
`id` VARCHAR(32) NOT NULL,
|
||||||
|
`file_name` VARCHAR(256) NOT NULL COMMENT '原始文件名',
|
||||||
|
`word_count` INT NOT NULL DEFAULT 0 COMMENT '替换词数量',
|
||||||
|
`content` TEXT COMMENT '文件原始内容',
|
||||||
|
`creator` BIGINT DEFAULT NULL,
|
||||||
|
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`updater` BIGINT DEFAULT NULL,
|
||||||
|
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
INDEX `idx_creator` (`creator`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='替换词文件';
|
||||||
|
|
||||||
|
-- 替换词词条表
|
||||||
|
CREATE TABLE IF NOT EXISTS `ai_agent_correct_word_item` (
|
||||||
|
`id` VARCHAR(32) NOT NULL,
|
||||||
|
`file_id` VARCHAR(32) NOT NULL COMMENT '所属文件ID',
|
||||||
|
`source_word` VARCHAR(128) NOT NULL COMMENT '原词',
|
||||||
|
`target_word` VARCHAR(128) NOT NULL COMMENT '替换词',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
INDEX `idx_file_id` (`file_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='替换词词条';
|
||||||
|
|
||||||
|
-- 智能体替换词文件关联表
|
||||||
|
CREATE TABLE IF NOT EXISTS `ai_agent_correct_word_mapping` (
|
||||||
|
`id` VARCHAR(32) NOT NULL,
|
||||||
|
`agent_id` VARCHAR(32) NOT NULL,
|
||||||
|
`file_id` VARCHAR(32) NOT NULL,
|
||||||
|
`creator` BIGINT DEFAULT NULL,
|
||||||
|
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`updater` BIGINT DEFAULT NULL,
|
||||||
|
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE INDEX `uk_agent_file` (`agent_id`, `file_id`),
|
||||||
|
INDEX `idx_agent_id` (`agent_id`),
|
||||||
|
INDEX `idx_file_id` (`file_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='智能体替换词文件关联';
|
||||||
@@ -620,6 +620,13 @@ databaseChangeLog:
|
|||||||
- sqlFile:
|
- sqlFile:
|
||||||
encoding: utf8
|
encoding: utf8
|
||||||
path: classpath:db/changelog/202604201719.sql
|
path: classpath:db/changelog/202604201719.sql
|
||||||
|
- changeSet:
|
||||||
|
id: 202604211515
|
||||||
|
author: rainv123
|
||||||
|
changes:
|
||||||
|
- sqlFile:
|
||||||
|
encoding: utf8
|
||||||
|
path: classpath:db/changelog/202604211515.sql
|
||||||
- changeSet:
|
- changeSet:
|
||||||
id: 202604211700
|
id: 202604211700
|
||||||
author: RanChen
|
author: RanChen
|
||||||
|
|||||||
@@ -209,4 +209,5 @@
|
|||||||
10200=\u6CA1\u6709\u6743\u9650\u67E5\u770B\u8BE5\u667A\u80FD\u4F53\u7684MCP\u63A5\u5165\u70B9\u5730\u5740
|
10200=\u6CA1\u6709\u6743\u9650\u67E5\u770B\u8BE5\u667A\u80FD\u4F53\u7684MCP\u63A5\u5165\u70B9\u5730\u5740
|
||||||
10201=\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u8FDB\u5165\u53C2\u6570\u7BA1\u7406\u914D\u7F6Emcp\u63A5\u5165\u70B9\u5730\u5740
|
10201=\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u8FDB\u5165\u53C2\u6570\u7BA1\u7406\u914D\u7F6Emcp\u63A5\u5165\u70B9\u5730\u5740
|
||||||
10202=\u6CA1\u6709\u6743\u9650\u67E5\u770B\u8BE5\u667A\u80FD\u4F53\u7684MCP\u5DE5\u5177\u5217\u8868
|
10202=\u6CA1\u6709\u6743\u9650\u67E5\u770B\u8BE5\u667A\u80FD\u4F53\u7684MCP\u5DE5\u5177\u5217\u8868
|
||||||
|
10203=\u6587\u4EF6\u540D\u79F0\u5DF2\u5B58\u5728
|
||||||
|
10204=\u6587\u4EF6\u5927\u5C0F\u8D85\u8FC71MB\u9650\u5236
|
||||||
@@ -209,4 +209,5 @@
|
|||||||
10200=Keine Berechtigung, die MCP-Endpunktadresse dieses Agenten anzuzeigen
|
10200=Keine Berechtigung, die MCP-Endpunktadresse dieses Agenten anzuzeigen
|
||||||
10201=Bitte kontaktieren Sie den Administrator, um die MCP-Endpunktadresse in der Parameterverwaltung zu konfigurieren
|
10201=Bitte kontaktieren Sie den Administrator, um die MCP-Endpunktadresse in der Parameterverwaltung zu konfigurieren
|
||||||
10202=Keine Berechtigung, die MCP-Tool-Liste dieses Agenten anzuzeigen
|
10202=Keine Berechtigung, die MCP-Tool-Liste dieses Agenten anzuzeigen
|
||||||
|
10203=Dateiname existiert bereits
|
||||||
|
10204=Dateigr\u00f6\u00dfe \u00fcberschreitet 1MB Limit
|
||||||
@@ -209,4 +209,6 @@
|
|||||||
10200=No permission to view the MCP endpoint address of this agent
|
10200=No permission to view the MCP endpoint address of this agent
|
||||||
10201=Please contact the administrator to configure the MCP endpoint address in parameter management
|
10201=Please contact the administrator to configure the MCP endpoint address in parameter management
|
||||||
10202=No permission to view the MCP tool list of this agent
|
10202=No permission to view the MCP tool list of this agent
|
||||||
|
10203=File name already exists
|
||||||
|
10204=File size exceeds 1MB limit
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
10015=Exclua primeiro os usu\u00E1rios do departamento
|
10015=Exclua primeiro os usu\u00E1rios do departamento
|
||||||
10016=Falha na implanta\u00E7\u00E3o, sem fluxo
|
10016=Falha na implanta\u00E7\u00E3o, sem fluxo
|
||||||
10017=Diagrama incorreto, verifique
|
10017=Diagrama incorreto, verifique
|
||||||
10018=Falha na exporta\u00E7\u00E3o, ID do modelo � {0}
|
10018=Falha na exporta\u00E7\u00E3o, ID do modelo � {0}
|
||||||
|
|
||||||
10019=Por favor, fa\u00E7a upload do arquivo
|
10019=Por favor, fa\u00E7a upload do arquivo
|
||||||
10020=Token n\u00E3o pode ser vazio
|
10020=Token n\u00E3o pode ser vazio
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
10046=Erro ao salvar impress\u00E3o vocal, entre em contato com o administrador
|
10046=Erro ao salvar impress\u00E3o vocal, entre em contato com o administrador
|
||||||
10047=Limite di\u00E1rio de envio atingido
|
10047=Limite di\u00E1rio de envio atingido
|
||||||
10048=Erro na inser\u00E7\u00E3o da senha antiga
|
10048=Erro na inser\u00E7\u00E3o da senha antiga
|
||||||
10049=LLM configurado n\u00E3o � openai ou Collama
|
10049=LLM configurado n\u00E3o � openai ou Collama
|
||||||
10050=Falha na gera\u00E7\u00E3o do token
|
10050=Falha na gera\u00E7\u00E3o do token
|
||||||
10051=Recurso n\u00E3o existe
|
10051=Recurso n\u00E3o existe
|
||||||
10052=Agente padr\u00E3o n\u00E3o encontrado
|
10052=Agente padr\u00E3o n\u00E3o encontrado
|
||||||
@@ -72,12 +72,12 @@
|
|||||||
10061=C\u00F3digo de ativa\u00E7\u00E3o n\u00E3o pode ser vazio
|
10061=C\u00F3digo de ativa\u00E7\u00E3o n\u00E3o pode ser vazio
|
||||||
10062=C\u00F3digo de ativa\u00E7\u00E3o incorreto
|
10062=C\u00F3digo de ativa\u00E7\u00E3o incorreto
|
||||||
10063=Dispositivo j\u00E1 ativado
|
10063=Dispositivo j\u00E1 ativado
|
||||||
10064=Este modelo � o modelo padr\u00E3o, configure primeiro outro modelo como padr\u00E3o
|
10064=Este modelo � o modelo padr\u00E3o, configure primeiro outro modelo como padr\u00E3o
|
||||||
10065=Falha ao adicionar dados
|
10065=Falha ao adicionar dados
|
||||||
10066=Falha ao modificar dados
|
10066=Falha ao modificar dados
|
||||||
10067=C\u00F3digo de verifica\u00E7\u00E3o gr\u00E1fico incorreto
|
10067=C\u00F3digo de verifica\u00E7\u00E3o gr\u00E1fico incorreto
|
||||||
10068=Registro por telefone n\u00E3o habilitado, fun\u00E7\u00E3o de c\u00F3digo de verifica\u00E7\u00E3o por SMS indispon\u00EDvel
|
10068=Registro por telefone n\u00E3o habilitado, fun\u00E7\u00E3o de c\u00F3digo de verifica\u00E7\u00E3o por SMS indispon\u00EDvel
|
||||||
10069=Nome de usu\u00E1rio n\u00E3o � n\u00FAmero de telefone, por favor insira novamente
|
10069=Nome de usu\u00E1rio n\u00E3o � n\u00FAmero de telefone, por favor insira novamente
|
||||||
10070=Este n\u00FAmero de telefone j\u00E1 est\u00E1 registrado
|
10070=Este n\u00FAmero de telefone j\u00E1 est\u00E1 registrado
|
||||||
10071=N\u00FAmero de telefone inserido n\u00E3o est\u00E1 registrado
|
10071=N\u00FAmero de telefone inserido n\u00E3o est\u00E1 registrado
|
||||||
10072=Registro de usu\u00E1rio comum n\u00E3o permitido atualmente
|
10072=Registro de usu\u00E1rio comum n\u00E3o permitido atualmente
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
10079=Par\u00E2metros de sele\u00E7\u00E3o incompat\u00EDveis entre LLM e reconhecimento de inten\u00E7\u00E3o Intent
|
10079=Par\u00E2metros de sele\u00E7\u00E3o incompat\u00EDveis entre LLM e reconhecimento de inten\u00E7\u00E3o Intent
|
||||||
10080=A pessoa ({0}) correspondente a esta impress\u00E3o vocal j\u00E1 est\u00E1 registrada, escolha outra voz para registrar
|
10080=A pessoa ({0}) correspondente a esta impress\u00E3o vocal j\u00E1 est\u00E1 registrada, escolha outra voz para registrar
|
||||||
10081=Erro ao excluir impress\u00E3o vocal
|
10081=Erro ao excluir impress\u00E3o vocal
|
||||||
10082=Esta modifica\u00E7\u00E3o n\u00E3o � permitida, esta voz j\u00E1 est\u00E1 registrada como impress\u00E3o vocal ({0})
|
10082=Esta modifica\u00E7\u00E3o n\u00E3o � permitida, esta voz j\u00E1 est\u00E1 registrada como impress\u00E3o vocal ({0})
|
||||||
10083=Erro ao modificar impress\u00E3o vocal, entre em contato com o administrador
|
10083=Erro ao modificar impress\u00E3o vocal, entre em contato com o administrador
|
||||||
10084=Erro no endere\u00E7o da interface de impress\u00E3o vocal, acesse o gerenciamento de par\u00E2metros para modificar o endere\u00E7o
|
10084=Erro no endere\u00E7o da interface de impress\u00E3o vocal, acesse o gerenciamento de par\u00E2metros para modificar o endere\u00E7o
|
||||||
10085=Dados de \u00E1udio n\u00E3o pertencem a este agente
|
10085=Dados de \u00E1udio n\u00E3o pertencem a este agente
|
||||||
@@ -205,7 +205,9 @@
|
|||||||
10196=Nome da etiqueta j\u00E1 existe
|
10196=Nome da etiqueta j\u00E1 existe
|
||||||
10197=Nome da etiqueta n\u00E3o pode ser vazio
|
10197=Nome da etiqueta n\u00E3o pode ser vazio
|
||||||
10198=Etiqueta n\u00E3o existe
|
10198=Etiqueta n\u00E3o existe
|
||||||
10199=Esta opera\u00E7\u00E3o n\u00E3o � suportada na an\u00E1lise de arquivos chineses
|
10199=Esta opera\u00E7\u00E3o n\u00E3o � suportada na an\u00E1lise de arquivos chineses
|
||||||
10200=Sem permiss\u00e3o para visualizar o endere\u00e7o do endpoint MCP deste agente
|
10200=Sem permiss\u00e3o para visualizar o endere\u00e7o do endpoint MCP deste agente
|
||||||
10201=Por favor, contate o administrador para configurar o endere\u00e7o do endpoint MCP no gerenciamento de par\u00e2metros
|
10201=Por favor, contate o administrador para configurar o endere\u00e7o do endpoint MCP no gerenciamento de par\u00e2metros
|
||||||
10202=Sem permiss\u00e3o para visualizar a lista de ferramentas MCP deste agente
|
10202=Sem permiss\u00e3o para visualizar a lista de ferramentas MCP deste agente
|
||||||
|
10203=Nome do arquivo j\u00E1 existe
|
||||||
|
10204=O tamanho do arquivo excede o limite de 1MB
|
||||||
@@ -208,4 +208,6 @@
|
|||||||
10199=T\u1EC7p \u0111ang \u0111\u01B0\u1EE3c ph\u00E2n t\u00EDch, thao t\u00E1c n\u00E0y kh\u00F4ng \u0111\u01B0\u1EE3c h\u1ED7 tr\u1EE3
|
10199=T\u1EC7p \u0111ang \u0111\u01B0\u1EE3c ph\u00E2n t\u00EDch, thao t\u00E1c n\u00E0y kh\u00F4ng \u0111\u01B0\u1EE3c h\u1ED7 tr\u1EE3
|
||||||
10200=Kh\u00f4ng c\u00f3 quy\u1ec1n xem \u0111\u1ecba ch\u1ec9 \u0111i\u1ec3m cu\u1ed1i MCP c\u1ee7a \u0111\u1ea1i l\u00fd n\u00e0y
|
10200=Kh\u00f4ng c\u00f3 quy\u1ec1n xem \u0111\u1ecba ch\u1ec9 \u0111i\u1ec3m cu\u1ed1i MCP c\u1ee7a \u0111\u1ea1i l\u00fd n\u00e0y
|
||||||
10201=Vui l\u00f2ng li\u00ean h\u1ec7 qu\u1ea3n tr\u1ecb vi\u00ean \u0111\u1ec3 c\u1ea5u h\u00ecnh \u0111\u1ecba ch\u1ec9 \u0111i\u1ec3m cu\u1ed1i MCP trong qu\u1ea3n l\u00fd tham s\u1ed1
|
10201=Vui l\u00f2ng li\u00ean h\u1ec7 qu\u1ea3n tr\u1ecb vi\u00ean \u0111\u1ec3 c\u1ea5u h\u00ecnh \u0111\u1ecba ch\u1ec9 \u0111i\u1ec3m cu\u1ed1i MCP trong qu\u1ea3n l\u00fd tham s\u1ed1
|
||||||
10202=Kh\u00f4ng c\u00f3 quy\u1ec1n xem danh s\u00e1ch c\u00f4ng c\u1ee5 MCP c\u1ee7a \u0111\u1ea1i l\u00fd n\u00e0y
|
10202=Kh\u00f4ng c\u00f3 quy\u1ec1n xem danh s\u00e1ch c\u00f4ng c\u1ee5 MCP c\u1ee7a \u0111\u1ea1i l\u00fd n\u00e0y
|
||||||
|
10203=T\u00EAn t\u1EC7p \u0111\u00E3 t\u1ED3n t\u1EA1i
|
||||||
|
10204=K\u00EDch th\u01b0\u1edbc t\u1ec7p v\u01b0\u1ee3t qu\u00e1 1MB
|
||||||
@@ -209,3 +209,5 @@
|
|||||||
10200=\u6CA1\u6709\u6743\u9650\u67E5\u770B\u8BE5\u667A\u80FD\u4F53\u7684MCP\u63A5\u5165\u70B9\u5730\u5740
|
10200=\u6CA1\u6709\u6743\u9650\u67E5\u770B\u8BE5\u667A\u80FD\u4F53\u7684MCP\u63A5\u5165\u70B9\u5730\u5740
|
||||||
10201=\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u8FDB\u5165\u53C2\u6570\u7BA1\u7406\u914D\u7F6Emcp\u63A5\u5165\u70B9\u5730\u5740
|
10201=\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u8FDB\u5165\u53C2\u6570\u7BA1\u7406\u914D\u7F6Emcp\u63A5\u5165\u70B9\u5730\u5740
|
||||||
10202=\u6CA1\u6709\u6743\u9650\u67E5\u770B\u8BE5\u667A\u80FD\u4F53\u7684MCP\u5DE5\u5177\u5217\u8868
|
10202=\u6CA1\u6709\u6743\u9650\u67E5\u770B\u8BE5\u667A\u80FD\u4F53\u7684MCP\u5DE5\u5177\u5217\u8868
|
||||||
|
10203=\u6587\u4EF6\u540D\u79F0\u5DF2\u5B58\u5728
|
||||||
|
10204=\u6587\u4EF6\u5927\u5C0F\u8D85\u8FC71MB\u9650\u5236
|
||||||
@@ -209,3 +209,5 @@
|
|||||||
10200=\u6C92\u6709\u6B0A\u9650\u67E5\u770B\u8A72\u667A\u80FD\u9AD4\u7684MCP\u63A5\u5165\u9EDE\u5730\u5740
|
10200=\u6C92\u6709\u6B0A\u9650\u67E5\u770B\u8A72\u667A\u80FD\u9AD4\u7684MCP\u63A5\u5165\u9EDE\u5730\u5740
|
||||||
10201=\u8ACB\u806F\u7E6B\u7BA1\u7406\u54E1\u9032\u5165\u53C3\u6578\u7BA1\u7406\u914D\u7F6Emcp\u63A5\u5165\u9EDE\u5730\u5740
|
10201=\u8ACB\u806F\u7E6B\u7BA1\u7406\u54E1\u9032\u5165\u53C3\u6578\u7BA1\u7406\u914D\u7F6Emcp\u63A5\u5165\u9EDE\u5730\u5740
|
||||||
10202=\u6C92\u6709\u6B0A\u9650\u67E5\u770B\u8A72\u667A\u80FD\u9AD4\u7684MCP\u5DE5\u5177\u5217\u8868
|
10202=\u6C92\u6709\u6B0A\u9650\u67E5\u770B\u8A72\u667A\u80FD\u9AD4\u7684MCP\u5DE5\u5177\u5217\u8868
|
||||||
|
10203=\u6A94\u6848\u540D\u7A31\u5DF2\u5B58\u5728
|
||||||
|
10204=\u6A94\u6848\u5927\u5C0F\u8D85\u904E1MB\u9650\u5236
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="xiaozhi.modules.agent.dao.AgentCorrectWordMappingDao">
|
||||||
|
|
||||||
|
<delete id="deleteByAgentId">
|
||||||
|
DELETE FROM ai_agent_correct_word_mapping WHERE agent_id = #{agentId}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<delete id="deleteByFileId">
|
||||||
|
DELETE FROM ai_agent_correct_word_mapping WHERE file_id = #{fileId}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<insert id="batchInsertMapping" parameterType="java.util.List">
|
||||||
|
INSERT INTO ai_agent_correct_word_mapping (id, agent_id, file_id, creator, created_at, updater, updated_at)
|
||||||
|
VALUES
|
||||||
|
<foreach collection="list" item="item" separator=",">
|
||||||
|
(#{item.id}, #{item.agentId}, #{item.fileId}, #{item.creator}, #{item.createdAt}, #{item.updater}, #{item.updatedAt})
|
||||||
|
</foreach>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<select id="selectByAgentId" resultType="xiaozhi.modules.agent.entity.AgentCorrectWordMappingEntity">
|
||||||
|
SELECT id, agent_id, file_id, creator, created_at, updater, updated_at
|
||||||
|
FROM ai_agent_correct_word_mapping
|
||||||
|
WHERE agent_id = #{agentId}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="xiaozhi.modules.correctword.dao.CorrectWordItemDao">
|
||||||
|
|
||||||
|
<insert id="batchInsert" parameterType="java.util.List">
|
||||||
|
INSERT INTO ai_agent_correct_word_item (id, file_id, source_word, target_word)
|
||||||
|
VALUES
|
||||||
|
<foreach collection="list" item="item" separator=",">
|
||||||
|
(#{item.id}, #{item.fileId}, #{item.sourceWord}, #{item.targetWord})
|
||||||
|
</foreach>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import os
|
import os
|
||||||
|
import asyncio
|
||||||
import yaml
|
import yaml
|
||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
from config.manage_api_client import init_service, get_server_config, get_agent_models
|
from config.manage_api_client import init_service, get_server_config, get_agent_models, get_correct_words
|
||||||
|
|
||||||
|
|
||||||
def get_project_dir():
|
def get_project_dir():
|
||||||
@@ -87,7 +88,16 @@ async def get_config_from_api_async(config):
|
|||||||
|
|
||||||
async def get_private_config_from_api(config, device_id, client_id):
|
async def get_private_config_from_api(config, device_id, client_id):
|
||||||
"""从Java API获取私有配置"""
|
"""从Java API获取私有配置"""
|
||||||
return await get_agent_models(device_id, client_id, config["selected_module"])
|
results = await asyncio.gather(
|
||||||
|
get_agent_models(device_id, client_id, config["selected_module"]),
|
||||||
|
get_correct_words(device_id),
|
||||||
|
return_exceptions=True
|
||||||
|
)
|
||||||
|
private_config = results[0] if not isinstance(results[0], Exception) else {}
|
||||||
|
correct_words = results[1] if not isinstance(results[1], Exception) else None
|
||||||
|
if correct_words:
|
||||||
|
private_config["correct_words"] = correct_words
|
||||||
|
return private_config
|
||||||
|
|
||||||
|
|
||||||
def ensure_directories(config):
|
def ensure_directories(config):
|
||||||
|
|||||||
@@ -183,6 +183,18 @@ async def get_agent_models(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_correct_words(mac_address: str) -> Optional[Dict]:
|
||||||
|
"""获取智能体替换词"""
|
||||||
|
try:
|
||||||
|
return await ManageApiClient._instance._execute_async_request(
|
||||||
|
"POST", "/config/correct-words",
|
||||||
|
json={"macAddress": mac_address}
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"获取替换词失败: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
async def generate_and_save_chat_summary(session_id: str) -> Optional[Dict]:
|
async def generate_and_save_chat_summary(session_id: str) -> Optional[Dict]:
|
||||||
"""生成并保存聊天记录总结"""
|
"""生成并保存聊天记录总结"""
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -726,6 +726,13 @@ class ConnectionHandler:
|
|||||||
if private_config.get("context_providers", None) is not None:
|
if private_config.get("context_providers", None) is not None:
|
||||||
self.config["context_providers"] = private_config["context_providers"]
|
self.config["context_providers"] = private_config["context_providers"]
|
||||||
|
|
||||||
|
# 注入替换词到 TTS 模块配置
|
||||||
|
if private_config.get("correct_words", None) is not None:
|
||||||
|
select_tts_module = self.config["selected_module"]["TTS"]
|
||||||
|
self.config["TTS"][select_tts_module]["correct_words"] = private_config[
|
||||||
|
"correct_words"
|
||||||
|
]
|
||||||
|
|
||||||
# 使用 run_in_executor 在线程池中执行 initialize_modules,避免阻塞主循环
|
# 使用 run_in_executor 在线程池中执行 initialize_modules,避免阻塞主循环
|
||||||
try:
|
try:
|
||||||
modules = await self.loop.run_in_executor(
|
modules = await self.loop.run_in_executor(
|
||||||
|
|||||||
@@ -194,6 +194,8 @@ class TTSProvider(TTSProviderBase):
|
|||||||
|
|
||||||
# 过滤Markdown
|
# 过滤Markdown
|
||||||
filtered_text = MarkdownCleaner.clean_markdown(text)
|
filtered_text = MarkdownCleaner.clean_markdown(text)
|
||||||
|
if self._correct_words_pattern:
|
||||||
|
filtered_text = self._correct_words_pattern.sub(lambda m: self.correct_words[m.group(0)], filtered_text)
|
||||||
|
|
||||||
if filtered_text:
|
if filtered_text:
|
||||||
# 发送continue-task消息
|
# 发送continue-task消息
|
||||||
@@ -480,6 +482,8 @@ class TTSProvider(TTSProviderBase):
|
|||||||
|
|
||||||
# 发送文本
|
# 发送文本
|
||||||
filtered_text = MarkdownCleaner.clean_markdown(text)
|
filtered_text = MarkdownCleaner.clean_markdown(text)
|
||||||
|
if self._correct_words_pattern:
|
||||||
|
filtered_text = self._correct_words_pattern.sub(lambda m: self.correct_words[m.group(0)], filtered_text)
|
||||||
# 发送continue-task消息
|
# 发送continue-task消息
|
||||||
continue_task_message = {
|
continue_task_message = {
|
||||||
"header": {
|
"header": {
|
||||||
|
|||||||
@@ -295,6 +295,8 @@ class TTSProvider(TTSProviderBase):
|
|||||||
logger.bind(tag=TAG).warning(f"WebSocket连接不存在,终止发送文本")
|
logger.bind(tag=TAG).warning(f"WebSocket连接不存在,终止发送文本")
|
||||||
return
|
return
|
||||||
filtered_text = MarkdownCleaner.clean_markdown(text)
|
filtered_text = MarkdownCleaner.clean_markdown(text)
|
||||||
|
if self._correct_words_pattern:
|
||||||
|
filtered_text = self._correct_words_pattern.sub(lambda m: self.correct_words[m.group(0)], filtered_text)
|
||||||
if filtered_text:
|
if filtered_text:
|
||||||
run_request = {
|
run_request = {
|
||||||
"header": {
|
"header": {
|
||||||
@@ -564,6 +566,8 @@ class TTSProvider(TTSProviderBase):
|
|||||||
|
|
||||||
# 发送文本合成请求
|
# 发送文本合成请求
|
||||||
filtered_text = MarkdownCleaner.clean_markdown(text)
|
filtered_text = MarkdownCleaner.clean_markdown(text)
|
||||||
|
if self._correct_words_pattern:
|
||||||
|
filtered_text = self._correct_words_pattern.sub(lambda m: self.correct_words[m.group(0)], filtered_text)
|
||||||
run_request = {
|
run_request = {
|
||||||
"header": {
|
"header": {
|
||||||
"message_id": uuid.uuid4().hex,
|
"message_id": uuid.uuid4().hex,
|
||||||
|
|||||||
@@ -45,6 +45,21 @@ class TTSProviderBase(ABC):
|
|||||||
self.report_on_last = False
|
self.report_on_last = False
|
||||||
# sentence_id 到文本的映射,用于流式TTS获取正确的字幕文本
|
# sentence_id 到文本的映射,用于流式TTS获取正确的字幕文本
|
||||||
self._sentence_text_map = {}
|
self._sentence_text_map = {}
|
||||||
|
# 加载替换词,用于一次性正则替换
|
||||||
|
raw_words = config.get("correct_words", [])
|
||||||
|
self.correct_words = {}
|
||||||
|
for item in raw_words:
|
||||||
|
parts = item.split("|", 1)
|
||||||
|
if len(parts) == 2:
|
||||||
|
self.correct_words[parts[0]] = parts[1]
|
||||||
|
# 构建正则表达式,使用最长匹配优先(排序后转义拼接)
|
||||||
|
if self.correct_words:
|
||||||
|
# 按key长度降序排列,长的先匹配,避免短词部分干扰
|
||||||
|
sorted_keys = sorted(self.correct_words.keys(), key=len, reverse=True)
|
||||||
|
pattern_str = '|'.join(re.escape(k) for k in sorted_keys)
|
||||||
|
self._correct_words_pattern = re.compile(pattern_str)
|
||||||
|
else:
|
||||||
|
self._correct_words_pattern = None
|
||||||
|
|
||||||
self.tts_text_buff = []
|
self.tts_text_buff = []
|
||||||
self.punctuations = (
|
self.punctuations = (
|
||||||
@@ -89,7 +104,12 @@ class TTSProviderBase(ABC):
|
|||||||
self.before_stop_play_files.append((file_audio, text))
|
self.before_stop_play_files.append((file_audio, text))
|
||||||
|
|
||||||
def to_tts_stream(self, text, opus_handler: Callable[[bytes], None] = None) -> None:
|
def to_tts_stream(self, text, opus_handler: Callable[[bytes], None] = None) -> None:
|
||||||
|
# 保留原始文本用于显示/上报
|
||||||
|
original_text = text
|
||||||
text = MarkdownCleaner.clean_markdown(text)
|
text = MarkdownCleaner.clean_markdown(text)
|
||||||
|
# 使用正则一次性替换,避免重复遍历和部分匹配问题
|
||||||
|
if self._correct_words_pattern:
|
||||||
|
text = self._correct_words_pattern.sub(lambda m: self.correct_words[m.group(0)], text)
|
||||||
max_repeat_time = 5
|
max_repeat_time = 5
|
||||||
if self.delete_audio_file:
|
if self.delete_audio_file:
|
||||||
# 需要删除文件的直接转为音频数据
|
# 需要删除文件的直接转为音频数据
|
||||||
@@ -97,7 +117,8 @@ class TTSProviderBase(ABC):
|
|||||||
try:
|
try:
|
||||||
audio_bytes = asyncio.run(self.text_to_speak(text, None))
|
audio_bytes = asyncio.run(self.text_to_speak(text, None))
|
||||||
if audio_bytes:
|
if audio_bytes:
|
||||||
self.tts_audio_queue.put((SentenceType.FIRST, None, text, getattr(self, 'current_sentence_id', None)))
|
# 使用原始文本用于显示/上报
|
||||||
|
self.tts_audio_queue.put((SentenceType.FIRST, None, original_text, getattr(self, 'current_sentence_id', None)))
|
||||||
audio_bytes_to_data_stream(
|
audio_bytes_to_data_stream(
|
||||||
audio_bytes,
|
audio_bytes,
|
||||||
file_type=self.audio_file_type,
|
file_type=self.audio_file_type,
|
||||||
@@ -111,16 +132,16 @@ class TTSProviderBase(ABC):
|
|||||||
max_repeat_time -= 1
|
max_repeat_time -= 1
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.bind(tag=TAG).warning(
|
logger.bind(tag=TAG).warning(
|
||||||
f"语音生成失败{5 - max_repeat_time + 1}次: {text},错误: {e}"
|
f"语音生成失败{5 - max_repeat_time + 1}次: {original_text},错误: {e}"
|
||||||
)
|
)
|
||||||
max_repeat_time -= 1
|
max_repeat_time -= 1
|
||||||
if max_repeat_time > 0:
|
if max_repeat_time > 0:
|
||||||
logger.bind(tag=TAG).info(
|
logger.bind(tag=TAG).info(
|
||||||
f"语音生成成功: {text},重试{5 - max_repeat_time}次"
|
f"语音生成成功: {original_text},重试{5 - max_repeat_time}次"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.bind(tag=TAG).error(
|
logger.bind(tag=TAG).error(
|
||||||
f"语音生成失败: {text},请检查网络或服务是否正常"
|
f"语音生成失败: {original_text},请检查网络或服务是否正常"
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
@@ -131,7 +152,7 @@ class TTSProviderBase(ABC):
|
|||||||
asyncio.run(self.text_to_speak(text, tmp_file))
|
asyncio.run(self.text_to_speak(text, tmp_file))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.bind(tag=TAG).warning(
|
logger.bind(tag=TAG).warning(
|
||||||
f"语音生成失败{5 - max_repeat_time + 1}次: {text},错误: {e}"
|
f"语音生成失败{5 - max_repeat_time + 1}次: {original_text},错误: {e}"
|
||||||
)
|
)
|
||||||
# 未执行成功,删除文件
|
# 未执行成功,删除文件
|
||||||
if os.path.exists(tmp_file):
|
if os.path.exists(tmp_file):
|
||||||
@@ -140,20 +161,24 @@ class TTSProviderBase(ABC):
|
|||||||
|
|
||||||
if max_repeat_time > 0:
|
if max_repeat_time > 0:
|
||||||
logger.bind(tag=TAG).info(
|
logger.bind(tag=TAG).info(
|
||||||
f"语音生成成功: {text}:{tmp_file},重试{5 - max_repeat_time}次"
|
f"语音生成成功: {original_text}:{tmp_file},重试{5 - max_repeat_time}次"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.bind(tag=TAG).error(
|
logger.bind(tag=TAG).error(
|
||||||
f"语音生成失败: {text},请检查网络或服务是否正常"
|
f"语音生成失败: {original_text},请检查网络或服务是否正常"
|
||||||
)
|
)
|
||||||
self.tts_audio_queue.put((SentenceType.FIRST, None, text, getattr(self, 'current_sentence_id', None)))
|
self.tts_audio_queue.put((SentenceType.FIRST, None, original_text, getattr(self, 'current_sentence_id', None)))
|
||||||
self._process_audio_file_stream(tmp_file, callback=opus_handler)
|
self._process_audio_file_stream(tmp_file, callback=opus_handler)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.bind(tag=TAG).error(f"Failed to generate TTS file: {e}")
|
logger.bind(tag=TAG).error(f"Failed to generate TTS file: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def to_tts(self, text):
|
def to_tts(self, text):
|
||||||
|
# 保留原始文本用于日志/显示
|
||||||
|
original_text = text
|
||||||
text = MarkdownCleaner.clean_markdown(text)
|
text = MarkdownCleaner.clean_markdown(text)
|
||||||
|
if self._correct_words_pattern:
|
||||||
|
text = self._correct_words_pattern.sub(lambda m: self.correct_words[m.group(0)], text)
|
||||||
max_repeat_time = 5
|
max_repeat_time = 5
|
||||||
if self.delete_audio_file:
|
if self.delete_audio_file:
|
||||||
# 需要删除文件的直接转为音频数据
|
# 需要删除文件的直接转为音频数据
|
||||||
@@ -174,16 +199,16 @@ class TTSProviderBase(ABC):
|
|||||||
max_repeat_time -= 1
|
max_repeat_time -= 1
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.bind(tag=TAG).warning(
|
logger.bind(tag=TAG).warning(
|
||||||
f"语音生成失败{5 - max_repeat_time + 1}次: {text},错误: {e}"
|
f"语音生成失败{5 - max_repeat_time + 1}次: {original_text},错误: {e}"
|
||||||
)
|
)
|
||||||
max_repeat_time -= 1
|
max_repeat_time -= 1
|
||||||
if max_repeat_time > 0:
|
if max_repeat_time > 0:
|
||||||
logger.bind(tag=TAG).info(
|
logger.bind(tag=TAG).info(
|
||||||
f"语音生成成功: {text},重试{5 - max_repeat_time}次"
|
f"语音生成成功: {original_text},重试{5 - max_repeat_time}次"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.bind(tag=TAG).error(
|
logger.bind(tag=TAG).error(
|
||||||
f"语音生成失败: {text},请检查网络或服务是否正常"
|
f"语音生成失败: {original_text},请检查网络或服务是否正常"
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
@@ -194,7 +219,7 @@ class TTSProviderBase(ABC):
|
|||||||
asyncio.run(self.text_to_speak(text, tmp_file))
|
asyncio.run(self.text_to_speak(text, tmp_file))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.bind(tag=TAG).warning(
|
logger.bind(tag=TAG).warning(
|
||||||
f"语音生成失败{5 - max_repeat_time + 1}次: {text},错误: {e}"
|
f"语音生成失败{5 - max_repeat_time + 1}次: {original_text},错误: {e}"
|
||||||
)
|
)
|
||||||
# 未执行成功,删除文件
|
# 未执行成功,删除文件
|
||||||
if os.path.exists(tmp_file):
|
if os.path.exists(tmp_file):
|
||||||
@@ -203,11 +228,11 @@ class TTSProviderBase(ABC):
|
|||||||
|
|
||||||
if max_repeat_time > 0:
|
if max_repeat_time > 0:
|
||||||
logger.bind(tag=TAG).info(
|
logger.bind(tag=TAG).info(
|
||||||
f"语音生成成功: {text}:{tmp_file},重试{5 - max_repeat_time}次"
|
f"语音生成成功: {original_text}:{tmp_file},重试{5 - max_repeat_time}次"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.bind(tag=TAG).error(
|
logger.bind(tag=TAG).error(
|
||||||
f"语音生成失败: {text},请检查网络或服务是否正常"
|
f"语音生成失败: {original_text},请检查网络或服务是否正常"
|
||||||
)
|
)
|
||||||
|
|
||||||
return tmp_file
|
return tmp_file
|
||||||
|
|||||||
@@ -370,6 +370,8 @@ class TTSProvider(TTSProviderBase):
|
|||||||
|
|
||||||
# 过滤Markdown
|
# 过滤Markdown
|
||||||
filtered_text = MarkdownCleaner.clean_markdown(text)
|
filtered_text = MarkdownCleaner.clean_markdown(text)
|
||||||
|
if self._correct_words_pattern:
|
||||||
|
filtered_text = self._correct_words_pattern.sub(lambda m: self.correct_words[m.group(0)], filtered_text)
|
||||||
|
|
||||||
if filtered_text:
|
if filtered_text:
|
||||||
# 发送文本
|
# 发送文本
|
||||||
|
|||||||
@@ -92,22 +92,25 @@ class TTSProvider(TTSProviderBase):
|
|||||||
def to_tts_single_stream(self, text, is_last=False):
|
def to_tts_single_stream(self, text, is_last=False):
|
||||||
try:
|
try:
|
||||||
max_repeat_time = 5
|
max_repeat_time = 5
|
||||||
|
original_text = text
|
||||||
text = MarkdownCleaner.clean_markdown(text)
|
text = MarkdownCleaner.clean_markdown(text)
|
||||||
|
if self._correct_words_pattern:
|
||||||
|
text = self._correct_words_pattern.sub(lambda m: self.correct_words[m.group(0)], text)
|
||||||
try:
|
try:
|
||||||
asyncio.run(self.text_to_speak(text, is_last))
|
asyncio.run(self.text_to_speak(text, is_last))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.bind(tag=TAG).warning(
|
logger.bind(tag=TAG).warning(
|
||||||
f"语音生成失败{5 - max_repeat_time + 1}次: {text},错误: {e}"
|
f"语音生成失败{5 - max_repeat_time + 1}次: {original_text},错误: {e}"
|
||||||
)
|
)
|
||||||
max_repeat_time -= 1
|
max_repeat_time -= 1
|
||||||
|
|
||||||
if max_repeat_time > 0:
|
if max_repeat_time > 0:
|
||||||
logger.bind(tag=TAG).info(
|
logger.bind(tag=TAG).info(
|
||||||
f"语音生成成功: {text},重试{5 - max_repeat_time}次"
|
f"语音生成成功: {original_text},重试{5 - max_repeat_time}次"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.bind(tag=TAG).error(
|
logger.bind(tag=TAG).error(
|
||||||
f"语音生成失败: {text},请检查网络或服务是否正常"
|
f"语音生成失败: {original_text},请检查网络或服务是否正常"
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.bind(tag=TAG).error(f"Failed to generate TTS file: {e}")
|
logger.bind(tag=TAG).error(f"Failed to generate TTS file: {e}")
|
||||||
@@ -203,6 +206,8 @@ class TTSProvider(TTSProviderBase):
|
|||||||
"""
|
"""
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
text = MarkdownCleaner.clean_markdown(text)
|
text = MarkdownCleaner.clean_markdown(text)
|
||||||
|
if self._correct_words_pattern:
|
||||||
|
text = self._correct_words_pattern.sub(lambda m: self.correct_words[m.group(0)], text)
|
||||||
|
|
||||||
payload = {"text": text, "character": self.voice}
|
payload = {"text": text, "character": self.voice}
|
||||||
|
|
||||||
|
|||||||
@@ -84,22 +84,25 @@ class TTSProvider(TTSProviderBase):
|
|||||||
def to_tts_single_stream(self, text, is_last=False):
|
def to_tts_single_stream(self, text, is_last=False):
|
||||||
try:
|
try:
|
||||||
max_repeat_time = 5
|
max_repeat_time = 5
|
||||||
|
original_text = text
|
||||||
text = MarkdownCleaner.clean_markdown(text)
|
text = MarkdownCleaner.clean_markdown(text)
|
||||||
|
if self._correct_words_pattern:
|
||||||
|
text = self._correct_words_pattern.sub(lambda m: self.correct_words[m.group(0)], text)
|
||||||
try:
|
try:
|
||||||
asyncio.run(self.text_to_speak(text, is_last))
|
asyncio.run(self.text_to_speak(text, is_last))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.bind(tag=TAG).warning(
|
logger.bind(tag=TAG).warning(
|
||||||
f"语音生成失败{5 - max_repeat_time + 1}次: {text},错误: {e}"
|
f"语音生成失败{5 - max_repeat_time + 1}次: {original_text},错误: {e}"
|
||||||
)
|
)
|
||||||
max_repeat_time -= 1
|
max_repeat_time -= 1
|
||||||
|
|
||||||
if max_repeat_time > 0:
|
if max_repeat_time > 0:
|
||||||
logger.bind(tag=TAG).info(
|
logger.bind(tag=TAG).info(
|
||||||
f"语音生成成功: {text},重试{5 - max_repeat_time}次"
|
f"语音生成成功: {original_text},重试{5 - max_repeat_time}次"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.bind(tag=TAG).error(
|
logger.bind(tag=TAG).error(
|
||||||
f"语音生成失败: {text},请检查网络或服务是否正常"
|
f"语音生成失败: {original_text},请检查网络或服务是否正常"
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.bind(tag=TAG).error(f"Failed to generate TTS file: {e}")
|
logger.bind(tag=TAG).error(f"Failed to generate TTS file: {e}")
|
||||||
@@ -202,6 +205,8 @@ class TTSProvider(TTSProviderBase):
|
|||||||
"""
|
"""
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
text = MarkdownCleaner.clean_markdown(text)
|
text = MarkdownCleaner.clean_markdown(text)
|
||||||
|
if self._correct_words_pattern:
|
||||||
|
text = self._correct_words_pattern.sub(lambda m: self.correct_words[m.group(0)], text)
|
||||||
|
|
||||||
params = {
|
params = {
|
||||||
"tts_text": text,
|
"tts_text": text,
|
||||||
|
|||||||
@@ -148,22 +148,25 @@ class TTSProvider(TTSProviderBase):
|
|||||||
def to_tts_single_stream(self, text, is_last=False):
|
def to_tts_single_stream(self, text, is_last=False):
|
||||||
try:
|
try:
|
||||||
max_repeat_time = 5
|
max_repeat_time = 5
|
||||||
|
original_text = text
|
||||||
text = MarkdownCleaner.clean_markdown(text)
|
text = MarkdownCleaner.clean_markdown(text)
|
||||||
|
if self._correct_words_pattern:
|
||||||
|
text = self._correct_words_pattern.sub(lambda m: self.correct_words[m.group(0)], text)
|
||||||
try:
|
try:
|
||||||
asyncio.run(self.text_to_speak(text, is_last))
|
asyncio.run(self.text_to_speak(text, is_last))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.bind(tag=TAG).warning(
|
logger.bind(tag=TAG).warning(
|
||||||
f"语音生成失败{5 - max_repeat_time + 1}次: {text},错误: {e}"
|
f"语音生成失败{5 - max_repeat_time + 1}次: {original_text},错误: {e}"
|
||||||
)
|
)
|
||||||
max_repeat_time -= 1
|
max_repeat_time -= 1
|
||||||
|
|
||||||
if max_repeat_time > 0:
|
if max_repeat_time > 0:
|
||||||
logger.bind(tag=TAG).info(
|
logger.bind(tag=TAG).info(
|
||||||
f"语音生成成功: {text},重试{5 - max_repeat_time}次"
|
f"语音生成成功: {original_text},重试{5 - max_repeat_time}次"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.bind(tag=TAG).error(
|
logger.bind(tag=TAG).error(
|
||||||
f"语音生成失败: {text},请检查网络或服务是否正常"
|
f"语音生成失败: {original_text},请检查网络或服务是否正常"
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.bind(tag=TAG).error(f"Failed to generate TTS file: {e}")
|
logger.bind(tag=TAG).error(f"Failed to generate TTS file: {e}")
|
||||||
@@ -298,6 +301,8 @@ class TTSProvider(TTSProviderBase):
|
|||||||
"""
|
"""
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
text = MarkdownCleaner.clean_markdown(text)
|
text = MarkdownCleaner.clean_markdown(text)
|
||||||
|
if self._correct_words_pattern:
|
||||||
|
text = self._correct_words_pattern.sub(lambda m: self.correct_words[m.group(0)], text)
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
"model": self.model,
|
"model": self.model,
|
||||||
|
|||||||
@@ -245,6 +245,8 @@ class TTSProvider(TTSProviderBase):
|
|||||||
return
|
return
|
||||||
|
|
||||||
filtered_text = MarkdownCleaner.clean_markdown(text)
|
filtered_text = MarkdownCleaner.clean_markdown(text)
|
||||||
|
if self._correct_words_pattern:
|
||||||
|
filtered_text = self._correct_words_pattern.sub(lambda m: self.correct_words[m.group(0)], filtered_text)
|
||||||
if filtered_text:
|
if filtered_text:
|
||||||
# 发送文本合成请求
|
# 发送文本合成请求
|
||||||
run_request = self._build_base_request(status=1,text=filtered_text)
|
run_request = self._build_base_request(status=1,text=filtered_text)
|
||||||
@@ -441,6 +443,8 @@ class TTSProvider(TTSProviderBase):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
filtered_text = MarkdownCleaner.clean_markdown(text)
|
filtered_text = MarkdownCleaner.clean_markdown(text)
|
||||||
|
if self._correct_words_pattern:
|
||||||
|
filtered_text = self._correct_words_pattern.sub(lambda m: self.correct_words[m.group(0)], filtered_text)
|
||||||
|
|
||||||
text_request = self._build_base_request(status=2,text=filtered_text)
|
text_request = self._build_base_request(status=2,text=filtered_text)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user