Commit b31e919d5235362a66e5a9bf7716b7a0990ad124

Authored by YunaiV
1 parent 20657cca

【新增】AI:流式发送消息的微调,统一成单接口

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,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 Content-Type: application/json 15 Content-Type: application/json
16 Authorization: {{token}} 16 Authorization: {{token}}
17 17
18 { 18 {
19 - "conversationId": "1781604279872581649", 19 + "conversationId": "1781604279872581651",
20 "content": "苹果是什么颜色?" 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,6 +17,7 @@ import reactor.core.publisher.Flux;
17 import java.util.List; 17 import java.util.List;
18 18
19 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 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 @Tag(name = "管理后台 - 聊天消息") 22 @Tag(name = "管理后台 - 聊天消息")
22 @RestController 23 @RestController
@@ -36,14 +37,8 @@ public class AiChatMessageController { @@ -36,14 +37,8 @@ public class AiChatMessageController {
36 @Operation(summary = "发送消息(流式)", description = "流式返回,响应较快") 37 @Operation(summary = "发送消息(流式)", description = "流式返回,响应较快")
37 @PostMapping(value = "/send-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) 38 @PostMapping(value = "/send-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
38 @PermitAll // 解决 SSE 最终响应的时候,会被 Access Denied 拦截的问题 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 @Operation(summary = "获得指定会话的消息列表") 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
@@ -44,4 +44,5 @@ public class AiChatMessageRespVO { @@ -44,4 +44,5 @@ public class AiChatMessageRespVO {
44 44
45 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-05-12 12:51") 45 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-05-12 12:51")
46 private LocalDateTime createTime; 46 private LocalDateTime createTime;
  47 +
47 } 48 }
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,12 +27,4 @@ public interface AiChatMessageConvert {
27 */ 27 */
28 List<AiChatMessageRespVO> convertAiChatMessageRespVOList(List<AiChatMessageDO> aiChatMessageDOList); 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,22 +23,6 @@ public interface AiChatService {
23 AiChatMessageRespVO chat(AiChatMessageSendReqVO sendReqVO); 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 * 获取 - 获取对话 message list 26 * 获取 - 获取对话 message list
43 * 27 *
44 * @param conversationId 28 * @param conversationId
@@ -54,4 +38,13 @@ public interface AiChatService { @@ -54,4 +38,13 @@ public interface AiChatService {
54 */ 38 */
55 Boolean deleteMessage(Long id); 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 package cn.iocoder.yudao.module.ai.service.impl; 1 package cn.iocoder.yudao.module.ai.service.impl;
2 2
3 import cn.hutool.core.exceptions.ExceptionUtil; 3 import cn.hutool.core.exceptions.ExceptionUtil;
  4 +import cn.hutool.core.util.ObjUtil;
4 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; 5 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
5 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; 7 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
6 import org.springframework.ai.chat.ChatClient; 8 import org.springframework.ai.chat.ChatClient;
7 import org.springframework.ai.chat.ChatResponse; 9 import org.springframework.ai.chat.ChatResponse;
8 import org.springframework.ai.chat.StreamingChatClient; 10 import org.springframework.ai.chat.StreamingChatClient;
9 import org.springframework.ai.chat.messages.MessageType; 11 import org.springframework.ai.chat.messages.MessageType;
  12 +import org.springframework.ai.chat.prompt.ChatOptionsBuilder;
10 import org.springframework.ai.chat.prompt.Prompt; 13 import org.springframework.ai.chat.prompt.Prompt;
11 -import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;  
12 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; 14 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
13 -import cn.iocoder.yudao.module.ai.ErrorCodeConstants;  
14 import cn.iocoder.yudao.module.ai.config.AiChatClientFactory; 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 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO; 16 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;
18 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO; 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 import cn.iocoder.yudao.module.ai.convert.AiChatMessageConvert; 18 import cn.iocoder.yudao.module.ai.convert.AiChatMessageConvert;
21 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; 19 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
22 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; 20 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
23 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; 21 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
24 -import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatConversationMapper;  
25 import cn.iocoder.yudao.module.ai.dal.mysql.AiChatMessageMapper; 22 import cn.iocoder.yudao.module.ai.dal.mysql.AiChatMessageMapper;
26 import cn.iocoder.yudao.module.ai.service.chat.AiChatConversationService; 23 import cn.iocoder.yudao.module.ai.service.chat.AiChatConversationService;
27 import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; 24 import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
@@ -33,13 +30,16 @@ import org.springframework.stereotype.Service; @@ -33,13 +30,16 @@ import org.springframework.stereotype.Service;
33 import org.springframework.transaction.annotation.Transactional; 30 import org.springframework.transaction.annotation.Transactional;
34 import reactor.core.publisher.Flux; 31 import reactor.core.publisher.Flux;
35 32
  33 +import java.time.LocalDateTime;
36 import java.util.List; 34 import java.util.List;
37 import java.util.Map; 35 import java.util.Map;
38 import java.util.Set; 36 import java.util.Set;
39 import java.util.concurrent.atomic.AtomicInteger; 37 import java.util.concurrent.atomic.AtomicInteger;
40 -import java.util.function.Consumer;  
41 import java.util.stream.Collectors; 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 * 聊天 service 44 * 聊天 service
45 * 45 *
@@ -52,11 +52,11 @@ import java.util.stream.Collectors; @@ -52,11 +52,11 @@ import java.util.stream.Collectors;
52 @AllArgsConstructor 52 @AllArgsConstructor
53 public class AiChatServiceImpl implements AiChatService { 53 public class AiChatServiceImpl implements AiChatService {
54 54
55 - private final AiChatClientFactory aiChatClientFactory; 55 + private final AiChatClientFactory chatClientFactory;
56 56
57 private final AiChatMessageMapper aiChatMessageMapper; 57 private final AiChatMessageMapper aiChatMessageMapper;
58 private final AiChatConversationService chatConversationService; 58 private final AiChatConversationService chatConversationService;
59 - private final AiChatModelService aiChatModalService; 59 + private final AiChatModelService chatModalService;
60 private final AiChatRoleService chatRoleService; 60 private final AiChatRoleService chatRoleService;
61 61
62 @Transactional(rollbackFor = Exception.class) 62 @Transactional(rollbackFor = Exception.class)
@@ -65,7 +65,7 @@ public class AiChatServiceImpl implements AiChatService { @@ -65,7 +65,7 @@ public class AiChatServiceImpl implements AiChatService {
65 // 查询对话 65 // 查询对话
66 AiChatConversationDO conversation = chatConversationService.validateExists(req.getConversationId()); 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 AiChatRoleDO chatRoleDO = conversation.getRoleId() != null ? chatRoleService.validateChatRole(conversation.getRoleId()) : null; 70 AiChatRoleDO chatRoleDO = conversation.getRoleId() != null ? chatRoleService.validateChatRole(conversation.getRoleId()) : null;
71 // 获取 client 类型 71 // 获取 client 类型
@@ -84,7 +84,7 @@ public class AiChatServiceImpl implements AiChatService { @@ -84,7 +84,7 @@ public class AiChatServiceImpl implements AiChatService {
84 // req.setTopP(req.getTopP()); 84 // req.setTopP(req.getTopP());
85 // req.setTemperature(req.getTemperature()); 85 // req.setTemperature(req.getTemperature());
86 // 发送 call 调用 86 // 发送 call 调用
87 - ChatClient chatClient = aiChatClientFactory.getChatClient(platformEnum); 87 + ChatClient chatClient = chatClientFactory.getChatClient(platformEnum);
88 ChatResponse call = chatClient.call(prompt); 88 ChatResponse call = chatClient.call(prompt);
89 content = call.getResult().getOutput().getContent(); 89 content = call.getResult().getOutput().getContent();
90 tokens = call.getResults().size(); 90 tokens = call.getResults().size();
@@ -113,88 +113,72 @@ public class AiChatServiceImpl implements AiChatService { @@ -113,88 +113,72 @@ public class AiChatServiceImpl implements AiChatService {
113 .setModelId(modelId) 113 .setModelId(modelId)
114 .setContent(content) 114 .setContent(content)
115 .setTokens(tokens) 115 .setTokens(tokens)
116 -  
117 .setTemperature(temperature) 116 .setTemperature(temperature)
118 .setMaxTokens(maxTokens) 117 .setMaxTokens(maxTokens)
119 .setMaxContexts(maxContexts); 118 .setMaxContexts(maxContexts);
  119 + insertChatMessageDO.setCreateTime(LocalDateTime.now());
120 // 增加 chat message 记录 120 // 增加 chat message 记录
121 aiChatMessageMapper.insert(insertChatMessageDO); 121 aiChatMessageMapper.insert(insertChatMessageDO);
122 return insertChatMessageDO; 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 0, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts()); 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 StringBuffer contentBuffer = new StringBuffer(); 152 StringBuffer contentBuffer = new StringBuffer();
153 - AtomicInteger tokens = new AtomicInteger(0); 153 + AtomicInteger tokens = new AtomicInteger(0); // TODO token 计算不对;
154 return streamResponse.map(res -> { 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 @Override 184 @Override
@@ -205,7 +189,7 @@ public class AiChatServiceImpl implements AiChatService { @@ -205,7 +189,7 @@ public class AiChatServiceImpl implements AiChatService {
205 List<AiChatMessageDO> aiChatMessageDOList = aiChatMessageMapper.selectByConversationId(conversationId); 189 List<AiChatMessageDO> aiChatMessageDOList = aiChatMessageMapper.selectByConversationId(conversationId);
206 // 获取模型信息 190 // 获取模型信息
207 Set<Long> modalIds = aiChatMessageDOList.stream().map(AiChatMessageDO::getModelId).collect(Collectors.toSet()); 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 Map<Long, AiChatModelDO> modalIdMap = modalList.stream().collect(Collectors.toMap(AiChatModelDO::getId, o -> o)); 193 Map<Long, AiChatModelDO> modalIdMap = modalList.stream().collect(Collectors.toMap(AiChatModelDO::getId, o -> o));
210 // 转换 AiChatMessageRespVO 194 // 转换 AiChatMessageRespVO
211 List<AiChatMessageRespVO> aiChatMessageRespList = AiChatMessageConvert.INSTANCE.convertAiChatMessageRespVOList(aiChatMessageDOList); 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,7 +94,10 @@ public class YiYanChatClient implements ChatClient, StreamingChatClient {
94 String a = ";"; 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 private YiYanChatCompletionRequest createRequest(Prompt prompt, boolean stream) { 103 private YiYanChatCompletionRequest createRequest(Prompt prompt, boolean stream) {