Commit f84d25d3b710f0788c0427f5f0d59739a291033d

Authored by cherishsince
1 parent 905ce773

stream 保存聊天记录

yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/ChatController.java
... ... @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.ai.controller;
2 2  
3 3 import cn.hutool.core.exceptions.ExceptionUtil;
4 4 import cn.iocoder.yudao.framework.ai.chat.ChatResponse;
5   -import cn.iocoder.yudao.framework.ai.config.AiClient;
6 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
7 6 import cn.iocoder.yudao.module.ai.service.ChatService;
8 7 import cn.iocoder.yudao.module.ai.vo.ChatReq;
... ... @@ -38,7 +37,6 @@ import java.util.function.Consumer;
38 37 public class ChatController {
39 38  
40 39 @Autowired
41   - private AiClient aiClient;
42 40 private final ChatService chatService;
43 41  
44 42 @Operation(summary = "聊天-chat", description = "这个一般等待时间比较久,需要全部完成才会返回!")
... ... @@ -52,30 +50,7 @@ public class ChatController {
52 50 @GetMapping(value = "/chatStream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
53 51 public SseEmitter chatStream(@Validated @ModelAttribute ChatReq req) {
54 52 Utf8SseEmitter sseEmitter = new Utf8SseEmitter();
55   - Flux<ChatResponse> streamResponse = chatService.chatStream(req);
56   - streamResponse.subscribe(
57   - new Consumer<ChatResponse>() {
58   - @Override
59   - public void accept(ChatResponse chatResponse) {
60   - String content = chatResponse.getResults().get(0).getOutput().getContent();
61   - try {
62   - sseEmitter.send(content, MediaType.APPLICATION_JSON);
63   - } catch (IOException e) {
64   - log.error("发送异常{}", ExceptionUtil.getMessage(e));
65   - // 如果不是因为关闭而抛出异常,则重新连接
66   - sseEmitter.completeWithError(e);
67   - }
68   - }
69   - },
70   - error -> {
71   - //
72   - log.error("subscribe错误 {}", ExceptionUtil.getMessage(error));
73   - },
74   - () -> {
75   - log.info("发送完成!");
76   - sseEmitter.complete();
77   - }
78   - );
  53 + chatService.chatStream(req, sseEmitter);
79 54 return sseEmitter;
80 55 }
81 56 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/ChatService.java
1 1 package cn.iocoder.yudao.module.ai.service;
2 2  
3   -import cn.iocoder.yudao.framework.ai.chat.ChatResponse;
4   -import cn.iocoder.yudao.module.ai.enums.AiClientNameEnum;
  3 +import cn.iocoder.yudao.module.ai.controller.Utf8SseEmitter;
5 4 import cn.iocoder.yudao.module.ai.vo.ChatReq;
6   -import reactor.core.publisher.Flux;
7 5  
8 6 /**
9 7 * 聊天 chat
... ... @@ -26,7 +24,8 @@ public interface ChatService {
26 24 * chat stream
27 25 *
28 26 * @param req
  27 + * @param sseEmitter
29 28 * @return
30 29 */
31   - Flux<ChatResponse> chatStream(ChatReq req);
  30 + void chatStream(ChatReq req, Utf8SseEmitter sseEmitter);
32 31 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/ChatServiceImpl.java
... ... @@ -8,6 +8,7 @@ import cn.iocoder.yudao.framework.ai.config.AiClient;
8 8 import cn.iocoder.yudao.framework.common.exception.ServerException;
9 9 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
10 10 import cn.iocoder.yudao.module.ai.ErrorCodeConstants;
  11 +import cn.iocoder.yudao.module.ai.controller.Utf8SseEmitter;
11 12 import cn.iocoder.yudao.module.ai.dataobject.AiChatConversationDO;
12 13 import cn.iocoder.yudao.module.ai.dataobject.AiChatMessageDO;
13 14 import cn.iocoder.yudao.module.ai.dataobject.AiChatRoleDO;
... ... @@ -21,10 +22,14 @@ import cn.iocoder.yudao.module.ai.service.ChatService;
21 22 import cn.iocoder.yudao.module.ai.vo.ChatReq;
22 23 import lombok.AllArgsConstructor;
23 24 import lombok.extern.slf4j.Slf4j;
  25 +import org.springframework.http.MediaType;
24 26 import org.springframework.stereotype.Service;
25 27 import org.springframework.transaction.annotation.Transactional;
26 28 import reactor.core.publisher.Flux;
27 29  
  30 +import java.io.IOException;
  31 +import java.util.function.Consumer;
  32 +
28 33 /**
29 34 * 聊天 service
30 35 *
... ... @@ -51,25 +56,17 @@ public class ChatServiceImpl implements ChatService {
51 56 */
52 57 @Transactional(rollbackFor = Exception.class)
53 58 public String chat(ChatReq req) {
  59 + Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
54 60 // 获取 client 类型
55 61 AiClientNameEnum clientNameEnum = AiClientNameEnum.valueOfName(req.getModal());
56 62 // 获取 对话类型(新建还是继续)
57 63 ChatConversationTypeEnum chatConversationTypeEnum = ChatConversationTypeEnum.valueOfType(req.getConversationType());
  64 + AiChatConversationDO aiChatConversationDO = getChatConversationNoExistToCreate(req, chatConversationTypeEnum, loginUserId);
58 65  
59   - AiChatConversationDO aiChatConversationDO;
60   - Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
61   - if (ChatConversationTypeEnum.NEW == chatConversationTypeEnum) {
62   - // 创建一个新的对话
63   - aiChatConversationDO = createNewChatConversation(req, loginUserId);
64   - } else {
65   - // 继续对话
66   - if (req.getConversationId() == null) {
67   - throw new ServerException(ErrorCodeConstants.AI_CHAT_CONTINUE_CONVERSATION_ID_NOT_NULL);
68   - }
69   - aiChatConversationDO = aiChatConversationMapper.selectById(req.getConversationId());
70   - }
  66 + // 保存 chat message
  67 + saveChatMessage(req, aiChatConversationDO.getId(), loginUserId);
71 68  
72   - String content;
  69 + String content = null;
73 70 try {
74 71 // 创建 chat 需要的 Prompt
75 72 Prompt prompt = new Prompt(req.getPrompt());
... ... @@ -81,13 +78,19 @@ public class ChatServiceImpl implements ChatService {
81 78 content = call.getResult().getOutput().getContent();
82 79 } catch (Exception e) {
83 80 content = ExceptionUtil.getMessage(e);
  81 + } finally {
  82 + // 保存 chat message
  83 + saveSystemChatMessage(req, aiChatConversationDO.getId(), loginUserId, content);
84 84 }
  85 + return content;
  86 + }
85 87  
  88 + private void saveChatMessage(ChatReq req, Long chatConversationId, Long loginUserId) {
86 89 // 增加 chat message 记录
87 90 aiChatMessageMapper.insert(
88 91 new AiChatMessageDO()
89 92 .setId(null)
90   - .setChatConversationId(aiChatConversationDO.getId())
  93 + .setChatConversationId(chatConversationId)
91 94 .setUserId(loginUserId)
92 95 .setMessage(req.getPrompt())
93 96 .setMessageType(MessageType.USER.getValue())
... ... @@ -98,7 +101,39 @@ public class ChatServiceImpl implements ChatService {
98 101  
99 102 // chat count 先+1
100 103 aiChatConversationMapper.updateIncrChatCount(req.getConversationId());
101   - return content;
  104 + }
  105 +
  106 + public void saveSystemChatMessage(ChatReq req, Long chatConversationId, Long loginUserId, String systemPrompts) {
  107 + // 增加 chat message 记录
  108 + aiChatMessageMapper.insert(
  109 + new AiChatMessageDO()
  110 + .setId(null)
  111 + .setChatConversationId(chatConversationId)
  112 + .setUserId(loginUserId)
  113 + .setMessage(systemPrompts)
  114 + .setMessageType(MessageType.SYSTEM.getValue())
  115 + .setTopK(req.getTopK())
  116 + .setTopP(req.getTopP())
  117 + .setTemperature(req.getTemperature())
  118 + );
  119 +
  120 + // chat count 先+1
  121 + aiChatConversationMapper.updateIncrChatCount(req.getConversationId());
  122 + }
  123 +
  124 + private AiChatConversationDO getChatConversationNoExistToCreate(ChatReq req, ChatConversationTypeEnum chatConversationTypeEnum, Long loginUserId) {
  125 + AiChatConversationDO aiChatConversationDO;
  126 + if (ChatConversationTypeEnum.NEW == chatConversationTypeEnum) {
  127 + // 创建一个新的对话
  128 + aiChatConversationDO = createNewChatConversation(req, loginUserId);
  129 + } else {
  130 + // 继续对话
  131 + if (req.getConversationId() == null) {
  132 + throw new ServerException(ErrorCodeConstants.AI_CHAT_CONTINUE_CONVERSATION_ID_NOT_NULL);
  133 + }
  134 + aiChatConversationDO = aiChatConversationMapper.selectById(req.getConversationId());
  135 + }
  136 + return aiChatConversationDO;
102 137 }
103 138  
104 139 private AiChatConversationDO createNewChatConversation(ChatReq req, Long loginUserId) {
... ... @@ -133,16 +168,52 @@ public class ChatServiceImpl implements ChatService {
133 168 * chat stream
134 169 *
135 170 * @param req
  171 + * @param sseEmitter
136 172 * @return
137 173 */
138 174 @Override
139   - public Flux<ChatResponse> chatStream(ChatReq req) {
  175 + public void chatStream(ChatReq req, Utf8SseEmitter sseEmitter) {
  176 + Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
  177 + // 获取 client 类型
140 178 AiClientNameEnum clientNameEnum = AiClientNameEnum.valueOfName(req.getModal());
  179 + // 获取 对话类型(新建还是继续)
  180 + ChatConversationTypeEnum chatConversationTypeEnum = ChatConversationTypeEnum.valueOfType(req.getConversationType());
  181 + AiChatConversationDO aiChatConversationDO = getChatConversationNoExistToCreate(req, chatConversationTypeEnum, loginUserId);
141 182 // 创建 chat 需要的 Prompt
142 183 Prompt prompt = new Prompt(req.getPrompt());
143 184 req.setTopK(req.getTopK());
144 185 req.setTopP(req.getTopP());
145 186 req.setTemperature(req.getTemperature());
146   - return aiClient.stream(prompt, clientNameEnum.getName());
  187 + // 保存 chat message
  188 + saveChatMessage(req, aiChatConversationDO.getId(), loginUserId);
  189 + Flux<ChatResponse> streamResponse = aiClient.stream(prompt, clientNameEnum.getName());
  190 +
  191 + StringBuffer contentBuffer = new StringBuffer();
  192 + streamResponse.subscribe(
  193 + new Consumer<ChatResponse>() {
  194 + @Override
  195 + public void accept(ChatResponse chatResponse) {
  196 + String content = chatResponse.getResults().get(0).getOutput().getContent();
  197 + try {
  198 + contentBuffer.append(content);
  199 + sseEmitter.send(content, MediaType.APPLICATION_JSON);
  200 + } catch (IOException e) {
  201 + log.error("发送异常{}", ExceptionUtil.getMessage(e));
  202 + // 如果不是因为关闭而抛出异常,则重新连接
  203 + sseEmitter.completeWithError(e);
  204 + }
  205 + }
  206 + },
  207 + error -> {
  208 + //
  209 + log.error("subscribe错误 {}", ExceptionUtil.getMessage(error));
  210 + },
  211 + () -> {
  212 + log.info("发送完成!");
  213 + sseEmitter.complete();
  214 + // 保存 chat message
  215 + saveSystemChatMessage(req, aiChatConversationDO.getId(), loginUserId, contentBuffer.toString());
  216 + }
  217 + );
147 218 }
148 219 }
... ...