Commit b31e919d5235362a66e5a9bf7716b7a0990ad124
1 parent
20657cca
【新增】AI:流式发送消息的微调,统一成单接口
Showing
9 changed files
with
121 additions
and
133 deletions
yudao-module-ai/yudao-module-ai-biz/src/main/resources/http/chat-message.http renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.http
| ... | ... | @@ -10,13 +10,13 @@ Authorization: {{token}} |
| 10 | 10 | } |
| 11 | 11 | |
| 12 | 12 | |
| 13 | -### chat call | |
| 14 | -POST {{baseUrl}}/admin-api/ai/chat/message/send-stream | |
| 13 | +### 发送消息(流式) | |
| 14 | +POST {{baseUrl}}/ai/chat/message/send-stream | |
| 15 | 15 | Content-Type: application/json |
| 16 | 16 | Authorization: {{token}} |
| 17 | 17 | |
| 18 | 18 | { |
| 19 | - "conversationId": "1781604279872581649", | |
| 19 | + "conversationId": "1781604279872581651", | |
| 20 | 20 | "content": "苹果是什么颜色?" |
| 21 | 21 | } |
| 22 | 22 | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java
| ... | ... | @@ -17,6 +17,7 @@ import reactor.core.publisher.Flux; |
| 17 | 17 | import java.util.List; |
| 18 | 18 | |
| 19 | 19 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 20 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | |
| 20 | 21 | |
| 21 | 22 | @Tag(name = "管理后台 - 聊天消息") |
| 22 | 23 | @RestController |
| ... | ... | @@ -36,14 +37,8 @@ public class AiChatMessageController { |
| 36 | 37 | @Operation(summary = "发送消息(流式)", description = "流式返回,响应较快") |
| 37 | 38 | @PostMapping(value = "/send-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) |
| 38 | 39 | @PermitAll // 解决 SSE 最终响应的时候,会被 Access Denied 拦截的问题 |
| 39 | - public Flux<AiChatMessageRespVO> sendMessageStream(@Validated @RequestBody AiChatMessageSendStreamReqVO sendReqVO) { | |
| 40 | - return chatService.chatStream(sendReqVO); | |
| 41 | - } | |
| 42 | - | |
| 43 | - @Operation(summary = "添加/提问", description = "先创建好 message 前端才好渲染") | |
| 44 | - @PostMapping(value = "/add") | |
| 45 | - public CommonResult<AiChatMessageRespVO> add(@Validated @RequestBody AiChatMessageAddReqVO req) { | |
| 46 | - return success(chatService.add(req)); | |
| 40 | + public Flux<AiChatMessageSendRespVO> sendChatMessageStream(@Validated @RequestBody AiChatMessageSendReqVO sendReqVO) { | |
| 41 | + return chatService.sendChatMessageStream(sendReqVO, getLoginUserId()); | |
| 47 | 42 | } |
| 48 | 43 | |
| 49 | 44 | @Operation(summary = "获得指定会话的消息列表") | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageRespVO.java
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageSendRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.time.LocalDateTime; | |
| 7 | + | |
| 8 | +@Schema(description = "管理后台 - AI 聊天消息发送 Response VO") | |
| 9 | +@Data | |
| 10 | +public class AiChatMessageSendRespVO { | |
| 11 | + | |
| 12 | + @Schema(description = "发送消息", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 13 | + private Message send; | |
| 14 | + | |
| 15 | + @Schema(description = "接收消息", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 16 | + private Message receive; | |
| 17 | + | |
| 18 | + @Schema(description = "消息") | |
| 19 | + @Data | |
| 20 | + public static class Message { | |
| 21 | + | |
| 22 | + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 23 | + private Long id; | |
| 24 | + | |
| 25 | + @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "role") | |
| 26 | + private String type; // 参见 MessageType 枚举类 | |
| 27 | + | |
| 28 | + @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊") | |
| 29 | + private String content; | |
| 30 | + | |
| 31 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-05-12 12:51") | |
| 32 | + private LocalDateTime createTime; | |
| 33 | + | |
| 34 | + } | |
| 35 | + | |
| 36 | +} | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageSendStreamReqVO.java deleted
| 1 | -package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message; | |
| 2 | - | |
| 3 | -import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | -import jakarta.validation.constraints.NotEmpty; | |
| 5 | -import jakarta.validation.constraints.NotNull; | |
| 6 | -import lombok.Data; | |
| 7 | - | |
| 8 | -@Schema(description = "管理后台 - AI 聊天消息发送 Request VO") | |
| 9 | -@Data | |
| 10 | -public class AiChatMessageSendStreamReqVO { | |
| 11 | - | |
| 12 | - @Schema(description = "提问的 messageId", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 13 | - @NotNull(message = "提问的 messageId 不能为空") | |
| 14 | - private Long id; | |
| 15 | - | |
| 16 | -} |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/convert/AiChatMessageConvert.java
| ... | ... | @@ -27,12 +27,4 @@ public interface AiChatMessageConvert { |
| 27 | 27 | */ |
| 28 | 28 | List<AiChatMessageRespVO> convertAiChatMessageRespVOList(List<AiChatMessageDO> aiChatMessageDOList); |
| 29 | 29 | |
| 30 | - /** | |
| 31 | - * 转换 - aiChatMessageDO | |
| 32 | - * | |
| 33 | - * @param aiChatMessageDO | |
| 34 | - * @return | |
| 35 | - */ | |
| 36 | - AiChatMessageRespVO convertAiChatMessageRespVO(AiChatMessageDO aiChatMessageDO); | |
| 37 | - | |
| 38 | 30 | } | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiChatService.java
| ... | ... | @@ -23,22 +23,6 @@ public interface AiChatService { |
| 23 | 23 | AiChatMessageRespVO chat(AiChatMessageSendReqVO sendReqVO); |
| 24 | 24 | |
| 25 | 25 | /** |
| 26 | - * chat stream | |
| 27 | - * | |
| 28 | - * @param sendReqVO | |
| 29 | - * @return | |
| 30 | - */ | |
| 31 | - Flux<AiChatMessageRespVO> chatStream(AiChatMessageSendStreamReqVO sendReqVO); | |
| 32 | - | |
| 33 | - /** | |
| 34 | - * 添加 - message | |
| 35 | - * | |
| 36 | - * @param sendReqVO | |
| 37 | - * @return | |
| 38 | - */ | |
| 39 | - AiChatMessageRespVO add(AiChatMessageAddReqVO sendReqVO); | |
| 40 | - | |
| 41 | - /** | |
| 42 | 26 | * 获取 - 获取对话 message list |
| 43 | 27 | * |
| 44 | 28 | * @param conversationId |
| ... | ... | @@ -54,4 +38,13 @@ public interface AiChatService { |
| 54 | 38 | */ |
| 55 | 39 | Boolean deleteMessage(Long id); |
| 56 | 40 | |
| 41 | + /** | |
| 42 | + * 发送消息 | |
| 43 | + * | |
| 44 | + * @param sendReqVO | |
| 45 | + * @param userId | |
| 46 | + * @return | |
| 47 | + */ | |
| 48 | + Flux<AiChatMessageSendRespVO> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO, Long userId); | |
| 49 | + | |
| 57 | 50 | } | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.ai.service.impl; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.exceptions.ExceptionUtil; |
| 4 | +import cn.hutool.core.util.ObjUtil; | |
| 4 | 5 | import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; |
| 6 | +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO; | |
| 5 | 7 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; |
| 6 | 8 | import org.springframework.ai.chat.ChatClient; |
| 7 | 9 | import org.springframework.ai.chat.ChatResponse; |
| 8 | 10 | import org.springframework.ai.chat.StreamingChatClient; |
| 9 | 11 | import org.springframework.ai.chat.messages.MessageType; |
| 12 | +import org.springframework.ai.chat.prompt.ChatOptionsBuilder; | |
| 10 | 13 | import org.springframework.ai.chat.prompt.Prompt; |
| 11 | -import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; | |
| 12 | 14 | import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; |
| 13 | -import cn.iocoder.yudao.module.ai.ErrorCodeConstants; | |
| 14 | 15 | import cn.iocoder.yudao.module.ai.config.AiChatClientFactory; |
| 15 | -import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO; | |
| 16 | -import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageAddReqVO; | |
| 17 | 16 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO; |
| 18 | 17 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO; |
| 19 | -import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendStreamReqVO; | |
| 20 | 18 | import cn.iocoder.yudao.module.ai.convert.AiChatMessageConvert; |
| 21 | 19 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; |
| 22 | 20 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; |
| 23 | 21 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; |
| 24 | -import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatConversationMapper; | |
| 25 | 22 | import cn.iocoder.yudao.module.ai.dal.mysql.AiChatMessageMapper; |
| 26 | 23 | import cn.iocoder.yudao.module.ai.service.chat.AiChatConversationService; |
| 27 | 24 | import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; |
| ... | ... | @@ -33,13 +30,16 @@ import org.springframework.stereotype.Service; |
| 33 | 30 | import org.springframework.transaction.annotation.Transactional; |
| 34 | 31 | import reactor.core.publisher.Flux; |
| 35 | 32 | |
| 33 | +import java.time.LocalDateTime; | |
| 36 | 34 | import java.util.List; |
| 37 | 35 | import java.util.Map; |
| 38 | 36 | import java.util.Set; |
| 39 | 37 | import java.util.concurrent.atomic.AtomicInteger; |
| 40 | -import java.util.function.Consumer; | |
| 41 | 38 | import java.util.stream.Collectors; |
| 42 | 39 | |
| 40 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | |
| 41 | +import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS; | |
| 42 | + | |
| 43 | 43 | /** |
| 44 | 44 | * 聊天 service |
| 45 | 45 | * |
| ... | ... | @@ -52,11 +52,11 @@ import java.util.stream.Collectors; |
| 52 | 52 | @AllArgsConstructor |
| 53 | 53 | public class AiChatServiceImpl implements AiChatService { |
| 54 | 54 | |
| 55 | - private final AiChatClientFactory aiChatClientFactory; | |
| 55 | + private final AiChatClientFactory chatClientFactory; | |
| 56 | 56 | |
| 57 | 57 | private final AiChatMessageMapper aiChatMessageMapper; |
| 58 | 58 | private final AiChatConversationService chatConversationService; |
| 59 | - private final AiChatModelService aiChatModalService; | |
| 59 | + private final AiChatModelService chatModalService; | |
| 60 | 60 | private final AiChatRoleService chatRoleService; |
| 61 | 61 | |
| 62 | 62 | @Transactional(rollbackFor = Exception.class) |
| ... | ... | @@ -65,7 +65,7 @@ public class AiChatServiceImpl implements AiChatService { |
| 65 | 65 | // 查询对话 |
| 66 | 66 | AiChatConversationDO conversation = chatConversationService.validateExists(req.getConversationId()); |
| 67 | 67 | // 获取对话模型 |
| 68 | - AiChatModelDO chatModel = aiChatModalService.validateChatModel(conversation.getModelId()); | |
| 68 | + AiChatModelDO chatModel = chatModalService.validateChatModel(conversation.getModelId()); | |
| 69 | 69 | // 获取角色信息 |
| 70 | 70 | AiChatRoleDO chatRoleDO = conversation.getRoleId() != null ? chatRoleService.validateChatRole(conversation.getRoleId()) : null; |
| 71 | 71 | // 获取 client 类型 |
| ... | ... | @@ -84,7 +84,7 @@ public class AiChatServiceImpl implements AiChatService { |
| 84 | 84 | // req.setTopP(req.getTopP()); |
| 85 | 85 | // req.setTemperature(req.getTemperature()); |
| 86 | 86 | // 发送 call 调用 |
| 87 | - ChatClient chatClient = aiChatClientFactory.getChatClient(platformEnum); | |
| 87 | + ChatClient chatClient = chatClientFactory.getChatClient(platformEnum); | |
| 88 | 88 | ChatResponse call = chatClient.call(prompt); |
| 89 | 89 | content = call.getResult().getOutput().getContent(); |
| 90 | 90 | tokens = call.getResults().size(); |
| ... | ... | @@ -113,88 +113,72 @@ public class AiChatServiceImpl implements AiChatService { |
| 113 | 113 | .setModelId(modelId) |
| 114 | 114 | .setContent(content) |
| 115 | 115 | .setTokens(tokens) |
| 116 | - | |
| 117 | 116 | .setTemperature(temperature) |
| 118 | 117 | .setMaxTokens(maxTokens) |
| 119 | 118 | .setMaxContexts(maxContexts); |
| 119 | + insertChatMessageDO.setCreateTime(LocalDateTime.now()); | |
| 120 | 120 | // 增加 chat message 记录 |
| 121 | 121 | aiChatMessageMapper.insert(insertChatMessageDO); |
| 122 | 122 | return insertChatMessageDO; |
| 123 | 123 | } |
| 124 | 124 | |
| 125 | - public Flux<AiChatMessageRespVO> chatStream(AiChatMessageSendStreamReqVO req) { | |
| 126 | - Long loginUserId = SecurityFrameworkUtils.getLoginUserId(); | |
| 127 | - // 查询提问的 message | |
| 128 | - AiChatMessageDO aiChatMessageDO = aiChatMessageMapper.selectById(req.getId()); | |
| 129 | - if (aiChatMessageDO == null) { | |
| 130 | - throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_CHAT_MESSAGE_NOT_EXIST); | |
| 125 | + @Override | |
| 126 | + public Flux<AiChatMessageSendRespVO> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO, Long userId) { | |
| 127 | + // 1.1 校验对话存在 | |
| 128 | + AiChatConversationDO conversation = chatConversationService.validateExists(sendReqVO.getConversationId()); | |
| 129 | + if (ObjUtil.notEqual(conversation.getUserId(), userId)) { | |
| 130 | + throw exception(CHAT_CONVERSATION_NOT_EXISTS); | |
| 131 | 131 | } |
| 132 | - // 查询对话 | |
| 133 | - AiChatConversationDO conversation = chatConversationService.validateExists(aiChatMessageDO.getConversationId()); | |
| 134 | - // 获取对话模型 | |
| 135 | - AiChatModelDO chatModel = aiChatModalService.validateChatModel(conversation.getModelId()); | |
| 136 | - // 获取角色信息 | |
| 137 | - AiChatRoleDO chatRoleDO = conversation.getRoleId() != null ? chatRoleService.validateChatRole(conversation.getRoleId()) : null; | |
| 138 | - // 创建 chat 需要的 Prompt | |
| 139 | - Prompt prompt = new Prompt(aiChatMessageDO.getContent()); | |
| 140 | - // 提前创建一个 system message | |
| 141 | - AiChatMessageDO systemMessage = insertChatMessage(conversation.getId(), MessageType.SYSTEM, loginUserId, conversation.getRoleId(), | |
| 142 | - chatModel.getModel(), chatModel.getId(), "", | |
| 132 | + // 1.2 校验模型 | |
| 133 | + AiChatModelDO model = chatModalService.validateChatModel(conversation.getModelId()); | |
| 134 | + AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform()); | |
| 135 | + StreamingChatClient chatClient = chatClientFactory.getStreamingChatClient(platform); | |
| 136 | + | |
| 137 | + // 2. 插入 user 发送消息 TODO tokens 计算 | |
| 138 | + AiChatMessageDO userMessage = insertChatMessage(conversation.getId(), MessageType.USER, userId, conversation.getRoleId(), | |
| 139 | + conversation.getModel(), conversation.getId(), sendReqVO.getContent(), | |
| 140 | + null, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts()); | |
| 141 | + | |
| 142 | + // 3.1 插入 system 接收消息 | |
| 143 | + AiChatMessageDO systemMessage = insertChatMessage(conversation.getId(), MessageType.SYSTEM, userId, conversation.getRoleId(), | |
| 144 | + conversation.getModel(), conversation.getId(), conversation.getSystemMessage(), | |
| 143 | 145 | 0, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts()); |
| 144 | -// req.setTopK(req.getTopK()); | |
| 145 | -// req.setTopP(req.getTopP()); | |
| 146 | -// req.setTemperature(req.getTemperature()); | |
| 147 | - // 获取 client 类型 | |
| 148 | - AiPlatformEnum platformEnum = AiPlatformEnum.validatePlatform(chatModel.getPlatform()); | |
| 149 | - StreamingChatClient streamingChatClient = aiChatClientFactory.getStreamingChatClient(platformEnum); | |
| 150 | - Flux<ChatResponse> streamResponse = streamingChatClient.stream(prompt); | |
| 151 | - // 转换 flex AiChatMessageRespVO | |
| 146 | + // 3.2 创建 chat 需要的 Prompt | |
| 147 | + // TODO 消息上下文 | |
| 148 | + Prompt prompt = new Prompt(sendReqVO.getContent()); | |
| 149 | +// ChatOptionsBuilder.builder().withTemperature(conversation.getTemperature().floatValue()).build() | |
| 150 | + Flux<ChatResponse> streamResponse = chatClient.stream(prompt); | |
| 151 | + // 3.3 转换 flex AiChatMessageRespVO | |
| 152 | 152 | StringBuffer contentBuffer = new StringBuffer(); |
| 153 | - AtomicInteger tokens = new AtomicInteger(0); | |
| 153 | + AtomicInteger tokens = new AtomicInteger(0); // TODO token 计算不对; | |
| 154 | 154 | return streamResponse.map(res -> { |
| 155 | - AiChatMessageRespVO aiChatMessageRespVO = | |
| 156 | - AiChatMessageConvert.INSTANCE.convertAiChatMessageRespVO(systemMessage); | |
| 157 | - aiChatMessageRespVO.setContent(res.getResult().getOutput().getContent()); | |
| 158 | - contentBuffer.append(res.getResult().getOutput().getContent()); | |
| 159 | - tokens.incrementAndGet(); | |
| 160 | - return aiChatMessageRespVO; | |
| 161 | - } | |
| 162 | - ).doOnComplete(new Runnable() { | |
| 163 | - @Override | |
| 164 | - public void run() { | |
| 165 | - log.info("发送完成!"); | |
| 166 | - // 保存 chat message | |
| 167 | - aiChatMessageMapper.updateById(new AiChatMessageDO() | |
| 168 | - .setId(systemMessage.getId()) | |
| 169 | - .setContent(contentBuffer.toString()) | |
| 170 | - .setTokens(tokens.get()) | |
| 171 | - ); | |
| 172 | - } | |
| 173 | - }).doOnError(new Consumer<Throwable>() { | |
| 174 | - @Override | |
| 175 | - public void accept(Throwable throwable) { | |
| 176 | - log.error("发送错误 {}!", throwable.getMessage()); | |
| 177 | - // 更新错误信息 | |
| 178 | - aiChatMessageMapper.updateById(new AiChatMessageDO() | |
| 179 | - .setId(systemMessage.getId()) | |
| 180 | - .setContent(throwable.getMessage()) | |
| 181 | - .setTokens(tokens.get()) | |
| 182 | - ); | |
| 183 | - } | |
| 184 | - }); | |
| 185 | - } | |
| 155 | + contentBuffer.append(res.getResult().getOutput().getContent()); | |
| 156 | + tokens.incrementAndGet(); | |
| 186 | 157 | |
| 187 | - @Override | |
| 188 | - public AiChatMessageRespVO add(AiChatMessageAddReqVO req) { | |
| 189 | - Long loginUserId = SecurityFrameworkUtils.getLoginUserId(); | |
| 190 | - // 查询对话 | |
| 191 | - AiChatConversationDO conversation = chatConversationService.validateExists(req.getConversationId()); | |
| 192 | - // 获取对话模型 | |
| 193 | - AiChatModelDO chatModel = aiChatModalService.validateChatModel(conversation.getModelId()); | |
| 194 | - AiChatMessageDO userMessage = insertChatMessage(conversation.getId(), MessageType.USER, loginUserId, conversation.getRoleId(), | |
| 195 | - chatModel.getModel(), chatModel.getId(), req.getContent(), | |
| 196 | - null, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts()); | |
| 197 | - return AiChatMessageConvert.INSTANCE.convertAiChatMessageRespVO(userMessage); | |
| 158 | + AiChatMessageSendRespVO.Message send = new AiChatMessageSendRespVO.Message().setId(userMessage.getId()) | |
| 159 | + .setType(MessageType.USER.getValue()).setCreateTime(userMessage.getCreateTime()) | |
| 160 | + .setContent(sendReqVO.getContent()); | |
| 161 | + AiChatMessageSendRespVO.Message receive = new AiChatMessageSendRespVO.Message().setId(systemMessage.getId()) | |
| 162 | + .setType(MessageType.SYSTEM.getValue()).setCreateTime(systemMessage.getCreateTime()) | |
| 163 | + .setContent(res.getResult().getOutput().getContent()); | |
| 164 | + return new AiChatMessageSendRespVO().setSend(send).setReceive(receive); | |
| 165 | + }).doOnComplete(() -> { | |
| 166 | + log.info("发送完成!"); | |
| 167 | + // 保存 chat message | |
| 168 | + aiChatMessageMapper.updateById(new AiChatMessageDO() | |
| 169 | + .setId(systemMessage.getId()) | |
| 170 | + .setContent(contentBuffer.toString()) | |
| 171 | + .setTokens(tokens.get()) | |
| 172 | + ); | |
| 173 | + }).doOnError(throwable -> { | |
| 174 | + log.error("发送错误 {}!", throwable.getMessage()); | |
| 175 | + // 更新错误信息 TODO 貌似不应该更新异常 | |
| 176 | + aiChatMessageMapper.updateById(new AiChatMessageDO() | |
| 177 | + .setId(systemMessage.getId()) | |
| 178 | + .setContent(throwable.getMessage()) | |
| 179 | + .setTokens(tokens.get()) | |
| 180 | + ); | |
| 181 | + }); | |
| 198 | 182 | } |
| 199 | 183 | |
| 200 | 184 | @Override |
| ... | ... | @@ -205,7 +189,7 @@ public class AiChatServiceImpl implements AiChatService { |
| 205 | 189 | List<AiChatMessageDO> aiChatMessageDOList = aiChatMessageMapper.selectByConversationId(conversationId); |
| 206 | 190 | // 获取模型信息 |
| 207 | 191 | Set<Long> modalIds = aiChatMessageDOList.stream().map(AiChatMessageDO::getModelId).collect(Collectors.toSet()); |
| 208 | - List<AiChatModelDO> modalList = aiChatModalService.getModalByIds(modalIds); | |
| 192 | + List<AiChatModelDO> modalList = chatModalService.getModalByIds(modalIds); | |
| 209 | 193 | Map<Long, AiChatModelDO> modalIdMap = modalList.stream().collect(Collectors.toMap(AiChatModelDO::getId, o -> o)); |
| 210 | 194 | // 转换 AiChatMessageRespVO |
| 211 | 195 | List<AiChatMessageRespVO> aiChatMessageRespList = AiChatMessageConvert.INSTANCE.convertAiChatMessageRespVOList(aiChatMessageDOList); | ... | ... |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/org/springframework/ai/models/yiyan/YiYanChatClient.java
| ... | ... | @@ -94,7 +94,10 @@ public class YiYanChatClient implements ChatClient, StreamingChatClient { |
| 94 | 94 | String a = ";"; |
| 95 | 95 | } |
| 96 | 96 | }); |
| 97 | - return response.map(res -> new ChatResponse(List.of(new Generation(res.getResult())))); | |
| 97 | + return response.map(res -> { | |
| 98 | + // TODO @fan:这里缺少了 usage 的封装 | |
| 99 | + return new ChatResponse(List.of(new Generation(res.getResult()))); | |
| 100 | + }); | |
| 98 | 101 | } |
| 99 | 102 | |
| 100 | 103 | private YiYanChatCompletionRequest createRequest(Prompt prompt, boolean stream) { | ... | ... |