Compare commits

...
21 Commits
Author SHA1 Message Date
Chingfeng Li c07ecbf6d1 在异步多轮对话中,会丢失FIRST LAST; FIRST与LAST每次对话都要有 2025-11-18 18:52:06 +08:00
hrzandGitHub 920e24b4ff Merge pull request #2547 from xinnan-tech/yinseyemian
补充图标调整
2025-11-17 19:06:59 +08:00
LiJinHui 43d82343a3 补充图标调整 2025-11-17 18:37:40 +08:00
hrzandGitHub faea50f99d Merge pull request #2545 from xinnan-tech/yinseyemian
调整试听按钮大小
2025-11-17 18:31:01 +08:00
hrzandGitHub ef59798d70 Merge pull request #2544 from xinnan-tech/i8n_fix_1
fix:修改错误简写和样式调整
2025-11-17 18:13:21 +08:00
LiJinHui c1f0853271 调整 2025-11-17 18:05:10 +08:00
rainv123 f9bd5bb995 fix:删除多余空格 2025-11-17 18:00:38 +08:00
rainv123 f1c2b5cb3f fix:重新修改简写 2025-11-17 17:41:12 +08:00
rainv123 579af29a73 fix:修改错误简写和样式调整 2025-11-17 17:30:27 +08:00
hrzandGitHub 9e20a53ca0 Merge pull request #2530 from xinnan-tech/yinseyemian
完善音色试听页面
2025-11-17 17:10:09 +08:00
hrzandGitHub 8768240efe Merge branch 'main' into yinseyemian 2025-11-17 17:09:53 +08:00
LJH-rgszeandGitHub b429760bd5 Merge pull request #2542 from xinnan-tech/i8n_fix_1
fix:翻译及样式优化
2025-11-17 17:01:41 +08:00
rainv123 218cd35c3d fix:修改 2025-11-17 16:57:45 +08:00
rainv123 146a039c19 fix:翻译及样式优化 2025-11-17 11:53:30 +08:00
LiJinHui 9a3e1fa9c8 补充 2025-11-17 11:39:11 +08:00
LiJinHui fa194af9bb 调整 2025-11-17 10:15:53 +08:00
LiJinHui 46b3757c13 补充 2025-11-15 16:36:59 +08:00
LiJinHui 5050ea7ac1 修复添加克隆音色后试听按钮不显示的bug 2025-11-15 16:06:58 +08:00
LiJinHui d121b4d0e1 删除多余代码 2025-11-15 10:48:52 +08:00
LiJinHui d45bf5f60a 补充 2025-11-15 10:45:22 +08:00
LiJinHui 94a43432fc 调整音色的前端页面,提供更方便的试听 2025-11-15 09:51:58 +08:00
12 changed files with 703 additions and 204 deletions
@@ -19,4 +19,15 @@ public class VoiceDTO implements Serializable {
@Schema(description = "音色名称") @Schema(description = "音色名称")
private String name; private String name;
@Schema(description = "音频播放地址")
private String voiceDemo;
// 添加双参数构造函数,保持向后兼容
public VoiceDTO(String id, String name) {
this.id = id;
this.name = name;
this.voiceDemo = null;
}
} }
@@ -133,7 +133,11 @@ public class TimbreServiceImpl extends BaseServiceImpl<TimbreDao, TimbreEntity>
timbreEntities = new ArrayList<>(); timbreEntities = new ArrayList<>();
} }
List<VoiceDTO> voiceDTOs = timbreEntities.stream() List<VoiceDTO> voiceDTOs = timbreEntities.stream()
.map(entity -> new VoiceDTO(entity.getId(), entity.getName())) .map(entity -> {
VoiceDTO dto = new VoiceDTO(entity.getId(), entity.getName());
dto.setVoiceDemo(entity.getVoiceDemo());
return dto;
})
.collect(Collectors.toList()); .collect(Collectors.toList());
// 获取当前登录用户ID // 获取当前登录用户ID
@@ -146,6 +150,8 @@ public class TimbreServiceImpl extends BaseServiceImpl<TimbreDao, TimbreEntity>
VoiceDTO voiceDTO = new VoiceDTO(); VoiceDTO voiceDTO = new VoiceDTO();
voiceDTO.setId(entity.getId()); voiceDTO.setId(entity.getId());
voiceDTO.setName(MessageUtils.getMessage(ErrorCode.VOICE_CLONE_PREFIX) + entity.getName()); voiceDTO.setName(MessageUtils.getMessage(ErrorCode.VOICE_CLONE_PREFIX) + entity.getName());
// 保留从数据库查询到的voiceDemo字段
voiceDTO.setVoiceDemo(entity.getVoiceDemo());
redisUtils.set(RedisKeys.getTimbreNameById(voiceDTO.getId()), voiceDTO.getName(), redisUtils.set(RedisKeys.getTimbreNameById(voiceDTO.getId()), voiceDTO.getName(),
RedisUtils.NOT_EXPIRE); RedisUtils.NOT_EXPIRE);
voiceDTOs.add(0, voiceDTO); voiceDTOs.add(0, voiceDTO);
@@ -205,6 +211,9 @@ public class TimbreServiceImpl extends BaseServiceImpl<TimbreDao, TimbreEntity>
if (list.isEmpty()) { if (list.isEmpty()) {
return null; return null;
} }
return new VoiceDTO(list.get(0).getId(), list.get(0).getName()); TimbreEntity entity = list.get(0);
VoiceDTO dto = new VoiceDTO(entity.getId(), entity.getName());
dto.setVoiceDemo(entity.getVoiceDemo());
return dto;
} }
} }
@@ -164,6 +164,9 @@ public class VoiceCloneServiceImpl extends BaseServiceImpl<VoiceCloneDao, VoiceC
if (entity.getUserId() != null) { if (entity.getUserId() != null) {
dto.setUserName(sysUserService.getByUserId(entity.getUserId()).getUsername()); dto.setUserName(sysUserService.getByUserId(entity.getUserId()).getUsername());
} }
// 确保trainStatus字段被正确设置,前端需要这个字段来判断是否为克隆音频
dto.setTrainStatus(entity.getTrainStatus());
return dto; return dto;
} }
@@ -197,6 +200,9 @@ public class VoiceCloneServiceImpl extends BaseServiceImpl<VoiceCloneDao, VoiceC
if (entity.getUserId() != null) { if (entity.getUserId() != null) {
dto.setUserName(sysUserService.getByUserId(entity.getUserId()).getUsername()); dto.setUserName(sysUserService.getByUserId(entity.getUserId()).getUsername());
} }
// 确保trainStatus字段被正确设置,前端需要这个字段来判断是否为克隆音频
dto.setTrainStatus(entity.getTrainStatus());
// 设置是否有音频数据 // 设置是否有音频数据
dto.setHasVoice(entity.getVoice() != null); dto.setHasVoice(entity.getVoice() != null);
@@ -164,7 +164,7 @@
10155=Thiếu cấu hình Huoshan Engine 10155=Thiếu cấu hình Huoshan Engine
10156=Lỗi định dạng phản hồi, thiếu trường BaseResp 10156=Lỗi định dạng phản hồi, thiếu trường BaseResp
10157=Yêu cầu thất bại 10157=Yêu cầu thất bại
10158=Nhân bản giọng nói: 10158=Nhân bản âm sắc:
10159=ID giọng nói đã tồn tại 10159=ID giọng nói đã tồn tại
10160=Lỗi định dạng ID giọng nói Huoshan Engine, phải bắt đầu bằng S_ 10160=Lỗi định dạng ID giọng nói Huoshan Engine, phải bắt đầu bằng S_
10161=Địa chỉ Mac đã tồn tại 10161=Địa chỉ Mac đã tồn tại
@@ -3,7 +3,7 @@
<mapper namespace="xiaozhi.modules.voiceclone.dao.VoiceCloneDao"> <mapper namespace="xiaozhi.modules.voiceclone.dao.VoiceCloneDao">
<select id="getTrainSuccess" resultType="xiaozhi.modules.model.dto.VoiceDTO"> <select id="getTrainSuccess" resultType="xiaozhi.modules.model.dto.VoiceDTO">
select id, name select id, name, voice_id as voiceDemo
from ai_voice_clone from ai_voice_clone
where model_id = #{modelId} and user_id = #{userId} and train_status = 2 where model_id = #{modelId} and user_id = #{userId} and train_status = 2
</select> </select>
+6 -6
View File
@@ -523,7 +523,7 @@ export default {
'dictManagement.pageTitle': 'Wörterbuchverwaltung', 'dictManagement.pageTitle': 'Wörterbuchverwaltung',
'dictManagement.searchPlaceholder': 'Bitte Wörterbuchwert-Label zur Suche eingeben', 'dictManagement.searchPlaceholder': 'Bitte Wörterbuchwert-Label zur Suche eingeben',
'dictManagement.search': 'Suchen', 'dictManagement.search': 'Suchen',
'dictManagement.dictTypeName': 'Wörterbuchtyp-Name', 'dictManagement.dictTypeName': 'WDT-Name',
'dictManagement.operation': 'Operation', 'dictManagement.operation': 'Operation',
'dictManagement.edit': 'Bearbeiten', 'dictManagement.edit': 'Bearbeiten',
'dictManagement.dictLabel': 'Wörterbuch-Label', 'dictManagement.dictLabel': 'Wörterbuch-Label',
@@ -532,10 +532,10 @@ export default {
'dictManagement.delete': 'Löschen', 'dictManagement.delete': 'Löschen',
'dictManagement.selectAll': 'Alle auswählen', 'dictManagement.selectAll': 'Alle auswählen',
'dictManagement.deselectAll': 'Alle abwählen', 'dictManagement.deselectAll': 'Alle abwählen',
'dictManagement.addDictType': 'Wörterbuchtyp hinzufügen', 'dictManagement.addDictType': 'WDT hinzufügen',
'dictManagement.batchDeleteDictType': 'Wörterbuchtypen massenhaft löschen', 'dictManagement.batchDeleteDictType': 'WDT massenhaft löschen',
'dictManagement.addDictData': 'Wörterbuchdaten hinzufügen', 'dictManagement.addDictData': 'WDT hinzufügen',
'dictManagement.batchDeleteDictData': 'Wörterbuchdaten massenhaft löschen', 'dictManagement.batchDeleteDictData': 'WDT massenhaft löschen',
'dictManagement.itemsPerPage': '{items} Einträge/Seite', 'dictManagement.itemsPerPage': '{items} Einträge/Seite',
'dictManagement.firstPage': 'Erste Seite', 'dictManagement.firstPage': 'Erste Seite',
'dictManagement.prevPage': 'Vorherige Seite', 'dictManagement.prevPage': 'Vorherige Seite',
@@ -810,7 +810,7 @@ export default {
'modelConfig.duplicate': 'Duplizieren', 'modelConfig.duplicate': 'Duplizieren',
'modelConfig.delete': 'Löschen', 'modelConfig.delete': 'Löschen',
'modelConfig.deselectAll': 'Alle abwählen', 'modelConfig.deselectAll': 'Alle abwählen',
'modelConfig.select': 'Auswählen', 'modelConfig.select': 'ausw.',
'modelConfig.selectAll': 'Alle auswählen', 'modelConfig.selectAll': 'Alle auswählen',
'modelConfig.add': 'Neu hinzufügen', 'modelConfig.add': 'Neu hinzufügen',
'modelConfig.selectModelsFirst': 'Bitte wählen Sie zuerst Modelle zum Löschen aus', 'modelConfig.selectModelsFirst': 'Bitte wählen Sie zuerst Modelle zum Löschen aus',
+3 -3
View File
@@ -10,7 +10,7 @@ export default {
'header.smartManagement': 'Tác nhân AI', 'header.smartManagement': 'Tác nhân AI',
'header.modelConfig': 'Mô hình', 'header.modelConfig': 'Mô hình',
'header.knowledgeBase': 'Kiến thức', 'header.knowledgeBase': 'Kiến thức',
'header.voiceCloneManagement': 'Nhân bản giọng', 'header.voiceCloneManagement': 'NBAS',
'header.voiceResourceManagement': 'Tài nguyên giọng nói', 'header.voiceResourceManagement': 'Tài nguyên giọng nói',
'header.userManagement': 'Người dùng', 'header.userManagement': 'Người dùng',
'header.otaManagement': 'Quản lý OTA', 'header.otaManagement': 'Quản lý OTA',
@@ -1043,7 +1043,7 @@ export default {
'sm2.publicKeyRetryFailed': 'Thử lấy lại khóa công khai thất bại', 'sm2.publicKeyRetryFailed': 'Thử lấy lại khóa công khai thất bại',
// Voice Clone and Resource Management // Voice Clone and Resource Management
'voiceClone.title': 'Nhân bản giọng nói', 'voiceClone.title': 'Nhân bản âm sắc',
'voiceResource.title': 'Tài nguyên giọng nói', 'voiceResource.title': 'Tài nguyên giọng nói',
'voiceClone.platformName': 'Tên nền tảng', 'voiceClone.platformName': 'Tên nền tảng',
'voiceClone.voiceId': 'ID giọng nói', 'voiceClone.voiceId': 'ID giọng nói',
@@ -1095,7 +1095,7 @@ export default {
'voiceClone.totalRecords': 'Tổng cộng {total} bản ghi', 'voiceClone.totalRecords': 'Tổng cộng {total} bản ghi',
'voiceClone.noVoiceCloneAssigned': 'Tài khoản của bạn không được chỉ định tài nguyên giọng nói', 'voiceClone.noVoiceCloneAssigned': 'Tài khoản của bạn không được chỉ định tài nguyên giọng nói',
'voiceClone.contactAdmin': 'Vui lòng liên hệ quản trị viên để được chỉ định tài nguyên giọng nói', 'voiceClone.contactAdmin': 'Vui lòng liên hệ quản trị viên để được chỉ định tài nguyên giọng nói',
'voiceClone.dialogTitle': 'Nhân bản giọng nói', 'voiceClone.dialogTitle': 'Nhân bản âm sắc',
'voiceClone.stepUpload': 'Chuẩn bị âm thanh', 'voiceClone.stepUpload': 'Chuẩn bị âm thanh',
'voiceClone.stepEdit': 'Chỉnh sửa âm thanh', 'voiceClone.stepEdit': 'Chỉnh sửa âm thanh',
'voiceClone.dragOrClick': 'Kéo tệp âm thanh vào đây hoặc nhấp để tải lên', 'voiceClone.dragOrClick': 'Kéo tệp âm thanh vào đây hoặc nhấp để tải lên',
+4
View File
@@ -15,4 +15,8 @@ select:-webkit-autofill:focus {
.el-footer { .el-footer {
height: 35px !important; /* 使用 !important 确保覆盖默认样式 */ height: 35px !important; /* 使用 !important 确保覆盖默认样式 */
}
.el-icon-video-play, .el-icon-video-pause {
font-size: 18px !important;
} }
@@ -42,7 +42,7 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('voiceClone.Details')" align="center" width="80"> <el-table-column :label="$t('voiceClone.Details')" align="center" width="120">
<template slot-scope="scope"> <template slot-scope="scope">
<el-tooltip :content="getTooltipContent(scope.row)" placement="top"> <el-tooltip :content="getTooltipContent(scope.row)" placement="top">
<el-button size="mini" type="text" icon="el-icon-info" <el-button size="mini" type="text" icon="el-icon-info"
+4 -2
View File
@@ -21,6 +21,8 @@
font-size: 25px; font-size: 25px;
text-align: left; text-align: left;
color: #3d4566; color: #3d4566;
white-space: nowrap;
flex-shrink: 0;
} }
.login-welcome { .login-welcome {
@@ -36,11 +38,11 @@
position: absolute; position: absolute;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
right: 18%; right: 15%;
background-color: #fff; background-color: #fff;
border-radius: 20px; border-radius: 20px;
padding: 35px 0; padding: 35px 0;
width: 450px; width: 550px;
box-sizing: border-box; box-sizing: border-box;
} }
File diff suppressed because it is too large Load Diff
+18 -16
View File
@@ -726,17 +726,18 @@ class ConnectionHandler:
self.logger.bind(tag=TAG).info(f"大模型收到用户消息: {query}") self.logger.bind(tag=TAG).info(f"大模型收到用户消息: {query}")
self.llm_finish_task = False self.llm_finish_task = False
# 为最顶层时新建会话ID和发送FIRST请求 # depth!=0目前只能表示是非用户发起的请求,与对话逻辑无关,这里所有tts对话都应该发送FIRST和LAST,没有例外
if depth == 0: if depth == 0:
self.sentence_id = str(uuid.uuid4().hex)
self.dialogue.put(Message(role="user", content=query)) self.dialogue.put(Message(role="user", content=query))
self.tts.tts_text_queue.put( # 发送开始标记
TTSMessageDTO( self.sentence_id = str(uuid.uuid4().hex)
sentence_id=self.sentence_id, self.tts.tts_text_queue.put(
sentence_type=SentenceType.FIRST, TTSMessageDTO(
content_type=ContentType.ACTION, sentence_id=self.sentence_id,
) sentence_type=SentenceType.FIRST,
content_type=ContentType.ACTION,
) )
)
# Define intent functions # Define intent functions
functions = None functions = None
@@ -826,6 +827,14 @@ class ConnectionHandler:
content_detail=content, content_detail=content,
) )
) )
# 发送结束标记
self.tts.tts_text_queue.put(
TTSMessageDTO(
sentence_id=self.sentence_id,
sentence_type=SentenceType.LAST,
content_type=ContentType.ACTION,
)
)
# 处理function call # 处理function call
if tool_call_flag: if tool_call_flag:
bHasError = False bHasError = False
@@ -879,14 +888,7 @@ class ConnectionHandler:
text_buff = "".join(response_message) text_buff = "".join(response_message)
self.tts_MessageText = text_buff self.tts_MessageText = text_buff
self.dialogue.put(Message(role="assistant", content=text_buff)) self.dialogue.put(Message(role="assistant", content=text_buff))
if depth == 0:
self.tts.tts_text_queue.put(
TTSMessageDTO(
sentence_id=self.sentence_id,
sentence_type=SentenceType.LAST,
content_type=ContentType.ACTION,
)
)
self.llm_finish_task = True self.llm_finish_task = True
# 使用lambda延迟计算,只有在DEBUG级别时才执行get_llm_dialogue() # 使用lambda延迟计算,只有在DEBUG级别时才执行get_llm_dialogue()
self.logger.bind(tag=TAG).debug( self.logger.bind(tag=TAG).debug(