|
|
@@ -5,6 +5,7 @@ import cn.hutool.core.util.StrUtil;
|
|
|
import cn.iocoder.byzs.framework.common.pojo.CommonResult;
|
|
|
import cn.iocoder.byzs.framework.common.util.object.BeanUtils;
|
|
|
import cn.iocoder.byzs.framework.tenant.core.util.TenantUtils;
|
|
|
+import cn.iocoder.byzs.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
|
|
|
import cn.iocoder.byzs.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
|
|
|
import cn.iocoder.byzs.module.ai.dal.dataobject.chat.AiChatConversationDO;
|
|
|
import cn.iocoder.byzs.module.ai.dal.dataobject.chat.AiChatMessageDO;
|
|
|
@@ -14,6 +15,7 @@ import cn.iocoder.byzs.module.ai.dal.mysql.chat.AiChatMessageMapper;
|
|
|
import cn.iocoder.byzs.module.ai.enums.ErrorCodeConstants;
|
|
|
import cn.iocoder.byzs.module.ai.enums.model.AiPlatformEnum;
|
|
|
import cn.iocoder.byzs.module.ai.service.chat.AiChatConversationService;
|
|
|
+import cn.iocoder.byzs.module.ai.service.chat.AiChatMessageService;
|
|
|
import cn.iocoder.byzs.module.ai.service.model.AiApiKeyService;
|
|
|
import cn.iocoder.byzs.module.ai.service.model.AiModelService;
|
|
|
import cn.iocoder.byzs.module.web.controller.admin.ai.vo.WebQSAiChatMessageSendReqVO;
|
|
|
@@ -97,6 +99,8 @@ public class WebQSAiServiceImpl {
|
|
|
@Resource
|
|
|
private AiChatConversationService chatConversationService;
|
|
|
@Resource
|
|
|
+ private AiChatMessageService chatMessageService;
|
|
|
+ @Resource
|
|
|
private AiChatMessageMapper chatMessageMapper;
|
|
|
@Resource
|
|
|
private AiModelService modelService;
|
|
|
@@ -114,6 +118,11 @@ public class WebQSAiServiceImpl {
|
|
|
public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(
|
|
|
WebQSAiChatMessageSendReqVO sendReqVO, Long userId, List<MultipartFile> multipartFiles) {
|
|
|
List<MultipartFile> files = multipartFiles == null ? List.of() : multipartFiles;
|
|
|
+ if (sendReqVO.getAttachments() == null || sendReqVO.getAttachments().isEmpty()) {
|
|
|
+ if (files.isEmpty()) {
|
|
|
+ return sendTextChatMessageStream(sendReqVO, userId);
|
|
|
+ }
|
|
|
+ }
|
|
|
int urlAttachmentCount = sendReqVO.getAttachments() == null ? 0 : sendReqVO.getAttachments().size();
|
|
|
if (urlAttachmentCount + files.size() > MAX_ATTACHMENTS) {
|
|
|
throw new IllegalArgumentException("单次最多上传 " + MAX_ATTACHMENTS + " 个附件");
|
|
|
@@ -205,6 +214,22 @@ public class WebQSAiServiceImpl {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 纯文本请求复用通用聊天链路,以获得其稳定的增量输出、连接复用和可选 TTS 能力。
|
|
|
+ */
|
|
|
+ private Flux<CommonResult<AiChatMessageSendRespVO>> sendTextChatMessageStream(
|
|
|
+ WebQSAiChatMessageSendReqVO sendReqVO, Long userId) {
|
|
|
+ if (StrUtil.isBlank(sendReqVO.getContent())) {
|
|
|
+ throw new IllegalArgumentException("文本问答内容不能为空");
|
|
|
+ }
|
|
|
+ AiChatMessageSendReqVO textSendReqVO = new AiChatMessageSendReqVO();
|
|
|
+ textSendReqVO.setConversationId(sendReqVO.getConversationId());
|
|
|
+ textSendReqVO.setContent(sendReqVO.getContent());
|
|
|
+ textSendReqVO.setUseContext(sendReqVO.getUseContext());
|
|
|
+ textSendReqVO.setPlayAudio(sendReqVO.getPlayAudio());
|
|
|
+ return chatMessageService.sendChatMessageStream(textSendReqVO, userId);
|
|
|
+ }
|
|
|
+
|
|
|
private void emitCompletedTextIfNecessary(StringBuilder answer, String completedText,
|
|
|
reactor.core.publisher.FluxSink<CommonResult<AiChatMessageSendRespVO>> sink,
|
|
|
AiChatMessageDO userMessage, AiChatMessageDO assistantMessage) {
|