Commit 5a4162cdc13bc9cb8939133cb24f1a9a936d3435

Authored by cherishsince
1 parent aaf1599f

【优化】优化 chat event stream 模式交互,增加 add message 优先记录

Showing 13 changed files with 155 additions and 233 deletions
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java
... ... @@ -36,5 +36,9 @@ public interface ErrorCodeConstants {
36 36 ErrorCode AI_CHAT_ROLE_NOT_EXIST = new ErrorCode(1_022_000_060, "AI 角色不存在!");
37 37 ErrorCode AI_CHAT_ROLE_NOT_PUBLIC = new ErrorCode(1_022_000_060, "AI 角色未公开!");
38 38  
  39 + // chat
  40 +
  41 + ErrorCode AI_CHAT_MESSAGE_NOT_EXIST = new ErrorCode(1_022_000_100, "AI 提问的 MessageId 不存在!");
  42 +
39 43  
40 44 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java
1 1 package cn.iocoder.yudao.module.ai.controller.admin.chat;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4   -import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;
5   -import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
  4 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.*;
6 5 import cn.iocoder.yudao.module.ai.service.AiChatService;
7 6 import io.swagger.v3.oas.annotations.Operation;
8 7 import io.swagger.v3.oas.annotations.Parameter;
... ... @@ -38,10 +37,16 @@ public class AiChatMessageController {
38 37 // TODO @fan:要不要使用 Flux 来返回;可以使用 Flux<AiChatMessageRespVO>
39 38 @Operation(summary = "发送消息(流式)", description = "流式返回,响应较快")
40 39 @PostMapping(value = "/send-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
41   - public Flux<AiChatMessageRespVO> sendMessageStream(@Validated @RequestBody AiChatMessageSendReqVO sendReqVO) {
  40 + public Flux<AiChatMessageRespVO> sendMessageStream(@Validated @RequestBody AiChatMessageSendStreamReqVO sendReqVO) {
42 41 return chatService.chatStream(sendReqVO);
43 42 }
44 43  
  44 + @Operation(summary = "添加/提问", description = "先创建好 message 前端才好渲染")
  45 + @PostMapping(value = "/add")
  46 + public CommonResult<AiChatMessageRespVO> add(@Validated @RequestBody AiChatMessageAddReqVO req) {
  47 + return success(chatService.add(req));
  48 + }
  49 +
45 50 @Operation(summary = "获得指定会话的消息列表")
46 51 @GetMapping("/list-by-conversation-id")
47 52 @Parameter(name = "conversationId", required = true, description = "会话编号", example = "1024")
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageAddReqVO.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 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 AiChatMessageAddReqVO {
  11 +
  12 + @Schema(description = "聊天对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  13 + @NotNull(message = "聊天对话编号不能为空")
  14 + private Long conversationId;
  15 +
  16 + @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "帮我写个 Java 算法")
  17 + @NotEmpty(message = "聊天内容不能为空")
  18 + private String content;
  19 +
  20 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageAddRespVO.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 聊天消息 Add Response VO")
  9 +@Data
  10 +public class AiChatMessageAddRespVO {
  11 +
  12 + @Schema(description = "用户信息")
  13 + private AiChatMessageRespVO userMessage;
  14 +
  15 + @Schema(description = "系统信息")
  16 + private AiChatMessageRespVO systemMessage;
  17 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageSendStreamReqVO.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 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
... ... @@ -26,4 +26,13 @@ public interface AiChatMessageConvert {
26 26 * @return
27 27 */
28 28 List<AiChatMessageRespVO> convertAiChatMessageRespVOList(List<AiChatMessageDO> aiChatMessageDOList);
  29 +
  30 + /**
  31 + * 转换 - aiChatMessageDO
  32 + *
  33 + * @param aiChatMessageDO
  34 + * @return
  35 + */
  36 + AiChatMessageRespVO convertAiChatMessageRespVO(AiChatMessageDO aiChatMessageDO);
  37 +
29 38 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatModelMapper.java
... ... @@ -11,7 +11,6 @@ import org.apache.ibatis.annotations.Mapper;
11 11  
12 12 import java.util.Collection;
13 13 import java.util.List;
14   -import java.util.Set;
15 14  
16 15 /**
17 16 * API 聊天模型 Mapper
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiChatModelService.java deleted
1   -package cn.iocoder.yudao.module.ai.service;
2   -
3   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
4   -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.*;
5   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
6   -
7   -import java.util.List;
8   -import java.util.Set;
9   -
10   -/**
11   - * ai modal
12   - *
13   - * @author fansili
14   - * @time 2024/4/24 19:42
15   - * @since 1.0
16   - */
17   -public interface AiChatModelService {
18   -
19   - /**
20   - * ai modal - 列表
21   - *
22   - * @param req
23   - * @return
24   - */
25   - PageResult<AiChatModelListRespVO> list(AiChatModelListReqVO req);
26   -
27   - /**
28   - * ai modal - 添加
29   - *
30   - * @param req
31   - */
32   - void add(AiChatModelAddReqVO req);
33   -
34   - /**
35   - * ai modal - 更新
36   - *
37   - * @param req
38   - */
39   - void update(AiChatModelUpdateReqVO req);
40   -
41   - /**
42   - * ai modal - 删除
43   - *
44   - * @param id
45   - */
46   - void delete(Long id);
47   -
48   - /**
49   - * 获取 - 获取 modal
50   - *
51   - * @param modalId
52   - * @return
53   - */
54   - AiChatModalRespVO getChatModalOfValidate(Long modalId);
55   -
56   - /**
57   - * 校验 - 是否存在
58   - *
59   - * @param id
60   - * @return
61   - */
62   - AiChatModelDO validateExists(Long id);
63   -
64   - /**
65   - * 校验 - 校验是否可用
66   - *
67   - * @param chatModal
68   - */
69   - void validateAvailable(AiChatModalRespVO chatModal);
70   -
71   - /**
72   - * 获取 - 根据 ids 批量获取
73   - *
74   - * @param modalIds
75   - * @return
76   - */
77   - List<AiChatModelDO> getModalByIds(Set<Long> modalIds);
78   -}
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiChatService.java
1 1 package cn.iocoder.yudao.module.ai.service;
2 2  
3   -import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;
4   -import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
  3 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.*;
5 4 import reactor.core.publisher.Flux;
6 5  
7 6 import java.util.List;
... ... @@ -29,7 +28,15 @@ public interface AiChatService {
29 28 * @param sendReqVO
30 29 * @return
31 30 */
32   - Flux<AiChatMessageRespVO> chatStream(AiChatMessageSendReqVO sendReqVO);
  31 + Flux<AiChatMessageRespVO> chatStream(AiChatMessageSendStreamReqVO sendReqVO);
  32 +
  33 + /**
  34 + * 添加 - message
  35 + *
  36 + * @param sendReqVO
  37 + * @return
  38 + */
  39 + AiChatMessageRespVO add(AiChatMessageAddReqVO sendReqVO);
33 40  
34 41 /**
35 42 * 获取 - 获取对话 message list
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatModalServiceImpl.java deleted
1   -package cn.iocoder.yudao.module.ai.service.impl;
2   -
3   -import cn.hutool.core.util.StrUtil;
4   -import cn.hutool.extra.validation.ValidationUtil;
5   -import cn.iocoder.yudao.framework.ai.AiPlatformEnum;
6   -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
7   -import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
8   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
9   -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
10   -import cn.iocoder.yudao.module.ai.ErrorCodeConstants;
11   -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.*;
12   -import cn.iocoder.yudao.module.ai.convert.AiChatModelConvert;
13   -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
14   -import cn.iocoder.yudao.module.ai.dal.mysql.AiChatModelMapper;
15   -import cn.iocoder.yudao.module.ai.dal.vo.AiChatModalConfigVO;
16   -import cn.iocoder.yudao.module.ai.service.AiChatModelService;
17   -import jakarta.validation.ConstraintViolation;
18   -import lombok.AllArgsConstructor;
19   -import lombok.extern.slf4j.Slf4j;
20   -import org.springframework.stereotype.Service;
21   -
22   -import java.util.List;
23   -import java.util.Set;
24   -
25   -/**
26   - * ai 模型
27   - *
28   - * @author fansili
29   - * @time 2024/4/24 19:42
30   - * @since 1.0
31   - */
32   -@AllArgsConstructor
33   -@Service
34   -@Slf4j
35   -public class AiChatModalServiceImpl implements AiChatModelService {
36   -
37   - private final AiChatModelMapper aiChatModelMapper;
38   -
39   - @Override
40   - public PageResult<AiChatModelListRespVO> list(AiChatModelListReqVO req) {
41   - LambdaQueryWrapperX<AiChatModelDO> queryWrapperX = new LambdaQueryWrapperX<>();
42   - // 查询的都是未禁用的模型
43   - queryWrapperX.eq(AiChatModelDO::getStatus, CommonStatusEnum.ENABLE.getStatus());
44   - // search
45   - if (!StrUtil.isBlank(req.getSearch())) {
46   - queryWrapperX.like(AiChatModelDO::getName, req.getSearch().trim());
47   - }
48   - // 默认排序
49   - queryWrapperX.orderByAsc(AiChatModelDO::getSort);
50   - // 查询
51   - PageResult<AiChatModelDO> aiChatModalDOPageResult = aiChatModelMapper.selectPage(req, queryWrapperX);
52   - // 转换 res
53   - List<AiChatModelListRespVO> resList = AiChatModelConvert.INSTANCE.convertAiChatModalListRes(aiChatModalDOPageResult.getList());
54   - return new PageResult<>(resList, aiChatModalDOPageResult.getTotal());
55   - }
56   -
57   - @Override
58   - public void add(AiChatModelAddReqVO req) {
59   - // 校验 platform、type
60   - validatePlatform(req.getPlatform());
61   - // 转换 do
62   - AiChatModelDO insertChatModalDO = AiChatModelConvert.INSTANCE.convertAiChatModalDO(req);
63   - // 设置默认属性
64   - insertChatModalDO.setStatus(CommonStatusEnum.ENABLE.getStatus());
65   - // 保存数据库
66   - aiChatModelMapper.insert(insertChatModalDO);
67   - }
68   -
69   - @Override
70   - public void update(AiChatModelUpdateReqVO req) {
71   - // 校验 platform
72   - validatePlatform(req.getPlatform());
73   - // 校验模型是否存在
74   - validateExists(req.getId());
75   - // 转换 updateChatModalDO
76   - AiChatModelDO updateChatModalDO = AiChatModelConvert.INSTANCE.convertAiChatModalDO(req);
77   - updateChatModalDO.setId(req.getId());
78   - // 更新数据库
79   - aiChatModelMapper.updateById(updateChatModalDO);
80   - }
81   -
82   - @Override
83   - public void delete(Long id) {
84   - // 检查 modal 是否存在
85   - validateExists(id);
86   - // 删除 delete
87   - aiChatModelMapper.deleteById(id);
88   - }
89   -
90   - @Override
91   - public AiChatModalRespVO getChatModalOfValidate(Long modalId) {
92   - // 检查 modal 是否存在
93   - AiChatModelDO aiChatModalDO = validateExists(modalId);
94   - return AiChatModelConvert.INSTANCE.convertAiChatModalRes(aiChatModalDO);
95   - }
96   -
97   - @Override
98   - public void validateAvailable(AiChatModalRespVO chatModal) {
99   - // 对话模型是否可用
100   - if (!CommonStatusEnum.ENABLE.getStatus().equals(chatModal.getStatus())) {
101   - throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MODAL_DISABLE_NOT_USED);
102   - }
103   - }
104   -
105   - @Override
106   - public List<AiChatModelDO> getModalByIds(Set<Long> modalIds) {
107   - return aiChatModelMapper.selectByIds(modalIds);
108   - }
109   -
110   - public AiChatModelDO validateExists(Long id) {
111   - AiChatModelDO aiChatModalDO = aiChatModelMapper.selectById(id);
112   - if (aiChatModalDO == null) {
113   - throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MODAL_NOT_EXIST);
114   - }
115   - return aiChatModalDO;
116   - }
117   -
118   - private void validatePlatform(String platform) {
119   - try {
120   - AiPlatformEnum.valueOfPlatform(platform);
121   - } catch (IllegalArgumentException e) {
122   - throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MODAL_PLATFORM_PARAMS_INCORRECT, e.getMessage());
123   - }
124   - }
125   -
126   - private void validateModalConfig(AiChatModalConfigVO aiChatModalConfigVO) {
127   - Set<ConstraintViolation<AiChatModalConfigVO>> validate = ValidationUtil.validate(aiChatModalConfigVO);
128   - for (ConstraintViolation<AiChatModalConfigVO> constraintViolation : validate) {
129   - throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MODAL_CONFIG_PARAMS_INCORRECT, constraintViolation.getMessage());
130   - }
131   - }
132   -}
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatServiceImpl.java
... ... @@ -7,11 +7,15 @@ import cn.iocoder.yudao.framework.ai.chat.ChatResponse;
7 7 import cn.iocoder.yudao.framework.ai.chat.StreamingChatClient;
8 8 import cn.iocoder.yudao.framework.ai.chat.messages.MessageType;
9 9 import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt;
  10 +import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
10 11 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
  12 +import cn.iocoder.yudao.module.ai.ErrorCodeConstants;
11 13 import cn.iocoder.yudao.module.ai.config.AiChatClientFactory;
12 14 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO;
  15 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageAddReqVO;
13 16 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;
14 17 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
  18 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendStreamReqVO;
15 19 import cn.iocoder.yudao.module.ai.convert.AiChatMessageConvert;
16 20 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
17 21 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
... ... @@ -19,11 +23,12 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
19 23 import cn.iocoder.yudao.module.ai.dal.mysql.AiChatConversationMapper;
20 24 import cn.iocoder.yudao.module.ai.dal.mysql.AiChatMessageMapper;
21 25 import cn.iocoder.yudao.module.ai.service.AiChatConversationService;
22   -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
23 26 import cn.iocoder.yudao.module.ai.service.AiChatRoleService;
24 27 import cn.iocoder.yudao.module.ai.service.AiChatService;
  28 +import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
25 29 import lombok.AllArgsConstructor;
26 30 import lombok.extern.slf4j.Slf4j;
  31 +import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
27 32 import org.springframework.stereotype.Service;
28 33 import org.springframework.transaction.annotation.Transactional;
29 34 import reactor.core.publisher.Flux;
... ... @@ -53,6 +58,7 @@ public class AiChatServiceImpl implements AiChatService {
53 58 private final AiChatConversationService chatConversationService;
54 59 private final AiChatModelService aiChatModalService;
55 60 private final AiChatRoleService aiChatRoleService;
  61 + private final HttpMessageConverters messageConverters;
56 62  
57 63 @Transactional(rollbackFor = Exception.class)
58 64 public AiChatMessageRespVO chat(AiChatMessageSendReqVO req) {
... ... @@ -124,10 +130,15 @@ public class AiChatServiceImpl implements AiChatService {
124 130 return insertChatMessageDO;
125 131 }
126 132  
127   - public Flux<AiChatMessageRespVO> chatStream(AiChatMessageSendReqVO req) {
  133 + public Flux<AiChatMessageRespVO> chatStream(AiChatMessageSendStreamReqVO req) {
128 134 Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
  135 + // 查询提问的 message
  136 + AiChatMessageDO aiChatMessageDO = aiChatMessageMapper.selectById(req.getId());
  137 + if (aiChatMessageDO == null) {
  138 + throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_CHAT_MESSAGE_NOT_EXIST);
  139 + }
129 140 // 查询对话
130   - AiChatConversationRespVO conversation = chatConversationService.getConversationOfValidate(req.getConversationId());
  141 + AiChatConversationRespVO conversation = chatConversationService.getConversationOfValidate(aiChatMessageDO.getConversationId());
131 142 // 获取对话模型
132 143 AiChatModelDO chatModel = aiChatModalService.validateChatModel(conversation.getModelId());
133 144 // 获取角色信息
... ... @@ -138,14 +149,14 @@ public class AiChatServiceImpl implements AiChatService {
138 149 // 校验角色是否公开
139 150 aiChatRoleService.validateIsPublic(aiChatRoleDO);
140 151 // 创建 chat 需要的 Prompt
141   - Prompt prompt = new Prompt(req.getContent());
  152 + Prompt prompt = new Prompt(aiChatMessageDO.getContent());
  153 + // 提前创建一个 system message
  154 + AiChatMessageDO systemMessage = insertChatMessage(conversation.getId(), MessageType.SYSTEM, loginUserId, conversation.getRoleId(),
  155 + chatModel.getModel(), chatModel.getId(), "",
  156 + 0, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts());
142 157 // req.setTopK(req.getTopK());
143 158 // req.setTopP(req.getTopP());
144 159 // req.setTemperature(req.getTemperature());
145   - // 保存 chat message
146   - insertChatMessage(conversation.getId(), MessageType.USER, loginUserId, conversation.getRoleId(),
147   - chatModel.getModel(), chatModel.getId(), req.getContent(),
148   - null, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts());
149 160 // 获取 client 类型
150 161 AiPlatformEnum platformEnum = AiPlatformEnum.validatePlatform(chatModel.getPlatform());
151 162 StreamingChatClient streamingChatClient = aiChatClientFactory.getStreamingChatClient(platformEnum);
... ... @@ -154,7 +165,8 @@ public class AiChatServiceImpl implements AiChatService {
154 165 StringBuffer contentBuffer = new StringBuffer();
155 166 AtomicInteger tokens = new AtomicInteger(0);
156 167 return streamResponse.map(res -> {
157   - AiChatMessageRespVO aiChatMessageRespVO = new AiChatMessageRespVO();
  168 + AiChatMessageRespVO aiChatMessageRespVO =
  169 + AiChatMessageConvert.INSTANCE.convertAiChatMessageRespVO(systemMessage);
158 170 aiChatMessageRespVO.setContent(res.getResult().getOutput().getContent());
159 171 contentBuffer.append(res.getResult().getOutput().getContent());
160 172 tokens.incrementAndGet();
... ... @@ -165,23 +177,47 @@ public class AiChatServiceImpl implements AiChatService {
165 177 public void run() {
166 178 log.info("发送完成!");
167 179 // 保存 chat message
168   - insertChatMessage(conversation.getId(), MessageType.SYSTEM, loginUserId, conversation.getRoleId(),
169   - chatModel.getModel(), chatModel.getId(), contentBuffer.toString(),
170   - tokens.get(), conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts());
  180 + aiChatMessageMapper.updateById(new AiChatMessageDO()
  181 + .setId(systemMessage.getId())
  182 + .setContent(contentBuffer.toString())
  183 + .setTokens(tokens.get())
  184 + );
171 185 }
172 186 }).doOnError(new Consumer<Throwable>() {
173 187 @Override
174 188 public void accept(Throwable throwable) {
175 189 log.error("发送错误 {}!", throwable.getMessage());
176   - // 保存 chat message
177   - insertChatMessage(conversation.getId(), MessageType.SYSTEM, loginUserId, conversation.getRoleId(),
178   - chatModel.getModel(), chatModel.getId(), throwable.getMessage(),
179   - tokens.get(), conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts());
  190 + // 更新错误信息
  191 + aiChatMessageMapper.updateById(new AiChatMessageDO()
  192 + .setId(systemMessage.getId())
  193 + .setContent(throwable.getMessage())
  194 + .setTokens(tokens.get())
  195 + );
180 196 }
181 197 });
182 198 }
183 199  
184 200 @Override
  201 + public AiChatMessageRespVO add(AiChatMessageAddReqVO req) {
  202 + Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
  203 + // 查询对话
  204 + AiChatConversationRespVO conversation = chatConversationService.getConversationOfValidate(req.getConversationId());
  205 + // 获取对话模型
  206 + AiChatModelDO chatModel = aiChatModalService.validateChatModel(conversation.getModelId());
  207 + // 获取角色信息
  208 + AiChatRoleDO aiChatRoleDO = null;
  209 + if (conversation.getRoleId() != null) {
  210 + aiChatRoleDO = aiChatRoleService.validateExists(conversation.getRoleId());
  211 + }
  212 + // 校验角色是否公开
  213 + aiChatRoleService.validateIsPublic(aiChatRoleDO);
  214 + AiChatMessageDO userMessage = insertChatMessage(conversation.getId(), MessageType.USER, loginUserId, conversation.getRoleId(),
  215 + chatModel.getModel(), chatModel.getId(), req.getContent(),
  216 + null, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts());
  217 + return AiChatMessageConvert.INSTANCE.convertAiChatMessageRespVO(userMessage);
  218 + }
  219 +
  220 + @Override
185 221 public List<AiChatMessageRespVO> getMessageListByConversationId(Long conversationId) {
186 222 // 校验对话是否存在
187 223 chatConversationService.validateExists(conversationId);
... ... @@ -207,4 +243,5 @@ public class AiChatServiceImpl implements AiChatService {
207 243 public Boolean deleteMessage(Long id) {
208 244 return aiChatMessageMapper.deleteById(id) > 0;
209 245 }
  246 +
210 247 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelService.java
... ... @@ -6,6 +6,9 @@ import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatMode
6 6 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
7 7 import jakarta.validation.Valid;
8 8  
  9 +import java.util.List;
  10 +import java.util.Set;
  11 +
9 12 /**
10 13 * AI 聊天模型 Service 接口
11 14 *
... ... @@ -60,4 +63,11 @@ public interface AiChatModelService {
60 63 */
61 64 AiChatModelDO validateChatModel(Long id);
62 65  
  66 + /**
  67 + * 获取 - 根据多个 ids 获取
  68 + *
  69 + * @param modalIds
  70 + * @return
  71 + */
  72 + List<AiChatModelDO> getModalByIds(Set<Long> modalIds);
63 73 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java
... ... @@ -12,6 +12,9 @@ import jakarta.annotation.Resource;
12 12 import org.springframework.stereotype.Service;
13 13 import org.springframework.validation.annotation.Validated;
14 14  
  15 +import java.util.List;
  16 +import java.util.Set;
  17 +
15 18 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
16 19 import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.*;
17 20  
... ... @@ -89,4 +92,9 @@ public class AiChatModelServiceImpl implements AiChatModelService {
89 92 return model;
90 93 }
91 94  
  95 + @Override
  96 + public List<AiChatModelDO> getModalByIds(Set<Long> modalIds) {
  97 + return chatModelMapper.selectByIds(modalIds);
  98 + }
  99 +
92 100 }
93 101 \ No newline at end of file
... ...