Commit eb8cd3d7367fe650209a7643f441eb7a663a1cc9

Authored by YunaiV
2 parents 9fbaada1 5a4162cd

Merge branch 'master-jdk21-ai' of https://gitee.com/cherishsince/ruoyi-vue-pro into master-jdk21-ai

# Conflicts:
#	yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatModelMapper.java
#	yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelService.java
#	yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java
Showing 12 changed files with 195 additions and 24 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,5 +36,9 @@ public interface ErrorCodeConstants {
36 ErrorCode AI_CHAT_ROLE_NOT_EXIST = new ErrorCode(1_022_000_060, "AI 角色不存在!"); 36 ErrorCode AI_CHAT_ROLE_NOT_EXIST = new ErrorCode(1_022_000_060, "AI 角色不存在!");
37 ErrorCode AI_CHAT_ROLE_NOT_PUBLIC = new ErrorCode(1_022_000_060, "AI 角色未公开!"); 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 package cn.iocoder.yudao.module.ai.controller.admin.chat; 1 package cn.iocoder.yudao.module.ai.controller.admin.chat;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 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 import cn.iocoder.yudao.module.ai.service.AiChatService; 5 import cn.iocoder.yudao.module.ai.service.AiChatService;
7 import io.swagger.v3.oas.annotations.Operation; 6 import io.swagger.v3.oas.annotations.Operation;
8 import io.swagger.v3.oas.annotations.Parameter; 7 import io.swagger.v3.oas.annotations.Parameter;
@@ -38,10 +37,16 @@ public class AiChatMessageController { @@ -38,10 +37,16 @@ public class AiChatMessageController {
38 // TODO @fan:要不要使用 Flux 来返回;可以使用 Flux<AiChatMessageRespVO> 37 // TODO @fan:要不要使用 Flux 来返回;可以使用 Flux<AiChatMessageRespVO>
39 @Operation(summary = "发送消息(流式)", description = "流式返回,响应较快") 38 @Operation(summary = "发送消息(流式)", description = "流式返回,响应较快")
40 @PostMapping(value = "/send-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) 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 return chatService.chatStream(sendReqVO); 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 @Operation(summary = "获得指定会话的消息列表") 50 @Operation(summary = "获得指定会话的消息列表")
46 @GetMapping("/list-by-conversation-id") 51 @GetMapping("/list-by-conversation-id")
47 @Parameter(name = "conversationId", required = true, description = "会话编号", example = "1024") 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/AiChatMessageRespVO.java
@@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message; @@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message;
3 import io.swagger.v3.oas.annotations.media.Schema; 3 import io.swagger.v3.oas.annotations.media.Schema;
4 import lombok.Data; 4 import lombok.Data;
5 5
  6 +import java.time.LocalDateTime;
  7 +
6 @Schema(description = "管理后台 - AI 聊天消息 Response VO") 8 @Schema(description = "管理后台 - AI 聊天消息 Response VO")
7 @Data 9 @Data
8 public class AiChatMessageRespVO { 10 public class AiChatMessageRespVO {
@@ -19,6 +21,9 @@ public class AiChatMessageRespVO { @@ -19,6 +21,9 @@ public class AiChatMessageRespVO {
19 @Schema(description = "用户编号", example = "4096") 21 @Schema(description = "用户编号", example = "4096")
20 private Long userId; // 仅当 user 发送时非空 22 private Long userId; // 仅当 user 发送时非空
21 23
  24 + @Schema(description = "用户头像", example = "http://xxx")
  25 + private Long avatarUrl; // 仅当 user 发送时非空
  26 +
22 @Schema(description = "角色编号", example = "888") 27 @Schema(description = "角色编号", example = "888")
23 private Long roleId; // 仅当 assistant 回复时非空 28 private Long roleId; // 仅当 assistant 回复时非空
24 29
@@ -28,10 +33,15 @@ public class AiChatMessageRespVO { @@ -28,10 +33,15 @@ public class AiChatMessageRespVO {
28 @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "123") 33 @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "123")
29 private Long modelId; 34 private Long modelId;
30 35
  36 + @Schema(description = "模型图片", requiredMode = Schema.RequiredMode.REQUIRED, example = "123")
  37 + private String modelImage;
  38 +
31 @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊") 39 @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊")
32 private String content; 40 private String content;
33 41
34 @Schema(description = "消耗 Token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "80") 42 @Schema(description = "消耗 Token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "80")
35 private Integer tokens; 43 private Integer tokens;
36 44
  45 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-05-12 12:51")
  46 + private LocalDateTime createTime;
37 } 47 }
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,4 +26,13 @@ public interface AiChatMessageConvert {
26 * @return 26 * @return
27 */ 27 */
28 List<AiChatMessageRespVO> convertAiChatMessageRespVOList(List<AiChatMessageDO> aiChatMessageDOList); 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,6 +11,9 @@ import org.apache.ibatis.annotations.Mapper; @@ -11,6 +11,9 @@ import org.apache.ibatis.annotations.Mapper;
11 11
12 import java.util.List; 12 import java.util.List;
13 13
  14 +import java.util.Collection;
  15 +import java.util.List;
  16 +
14 /** 17 /**
15 * API 聊天模型 Mapper 18 * API 聊天模型 Mapper
16 * 19 *
@@ -36,6 +39,17 @@ public interface AiChatModelMapper extends BaseMapperX&lt;AiChatModelDO&gt; { @@ -36,6 +39,17 @@ public interface AiChatModelMapper extends BaseMapperX&lt;AiChatModelDO&gt; {
36 return pageResult.getList().get(0); 39 return pageResult.getList().get(0);
37 } 40 }
38 41
  42 + /**
  43 + * 查询 - 根据 ids
  44 + *
  45 + * @param modalIds
  46 + * @return
  47 + */
  48 + default List<AiChatModelDO> selectByIds(Collection<Long> modalIds) {
  49 + return this.selectList(new LambdaQueryWrapperX<AiChatModelDO>().eq(AiChatModelDO::getId, modalIds));
  50 + }
  51 +
  52 +
39 default PageResult<AiChatModelDO> selectPage(AiChatModelPageReqVO reqVO) { 53 default PageResult<AiChatModelDO> selectPage(AiChatModelPageReqVO reqVO) {
40 return selectPage(reqVO, new LambdaQueryWrapperX<AiChatModelDO>() 54 return selectPage(reqVO, new LambdaQueryWrapperX<AiChatModelDO>()
41 .likeIfPresent(AiChatModelDO::getName, reqVO.getName()) 55 .likeIfPresent(AiChatModelDO::getName, reqVO.getName())
@@ -49,5 +63,4 @@ public interface AiChatModelMapper extends BaseMapperX&lt;AiChatModelDO&gt; { @@ -49,5 +63,4 @@ public interface AiChatModelMapper extends BaseMapperX&lt;AiChatModelDO&gt; {
49 .eq(AiChatModelDO::getStatus, status) 63 .eq(AiChatModelDO::getStatus, status)
50 .orderByAsc(AiChatModelDO::getSort)); 64 .orderByAsc(AiChatModelDO::getSort));
51 } 65 }
52 -  
53 } 66 }
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiChatService.java
1 package cn.iocoder.yudao.module.ai.service; 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 import reactor.core.publisher.Flux; 4 import reactor.core.publisher.Flux;
6 5
7 import java.util.List; 6 import java.util.List;
@@ -29,7 +28,15 @@ public interface AiChatService { @@ -29,7 +28,15 @@ public interface AiChatService {
29 * @param sendReqVO 28 * @param sendReqVO
30 * @return 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 * 获取 - 获取对话 message list 42 * 获取 - 获取对话 message list
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,11 +7,15 @@ import cn.iocoder.yudao.framework.ai.chat.ChatResponse;
7 import cn.iocoder.yudao.framework.ai.chat.StreamingChatClient; 7 import cn.iocoder.yudao.framework.ai.chat.StreamingChatClient;
8 import cn.iocoder.yudao.framework.ai.chat.messages.MessageType; 8 import cn.iocoder.yudao.framework.ai.chat.messages.MessageType;
9 import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt; 9 import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt;
  10 +import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
10 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; 11 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
  12 +import cn.iocoder.yudao.module.ai.ErrorCodeConstants;
11 import cn.iocoder.yudao.module.ai.config.AiChatClientFactory; 13 import cn.iocoder.yudao.module.ai.config.AiChatClientFactory;
12 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO; 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 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;
14 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;
  18 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendStreamReqVO;
15 import cn.iocoder.yudao.module.ai.convert.AiChatMessageConvert; 19 import cn.iocoder.yudao.module.ai.convert.AiChatMessageConvert;
16 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; 20 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
17 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; 21 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
@@ -19,18 +23,22 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; @@ -19,18 +23,22 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
19 import cn.iocoder.yudao.module.ai.dal.mysql.AiChatConversationMapper; 23 import cn.iocoder.yudao.module.ai.dal.mysql.AiChatConversationMapper;
20 import cn.iocoder.yudao.module.ai.dal.mysql.AiChatMessageMapper; 24 import cn.iocoder.yudao.module.ai.dal.mysql.AiChatMessageMapper;
21 import cn.iocoder.yudao.module.ai.service.AiChatConversationService; 25 import cn.iocoder.yudao.module.ai.service.AiChatConversationService;
22 -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;  
23 import cn.iocoder.yudao.module.ai.service.AiChatRoleService; 26 import cn.iocoder.yudao.module.ai.service.AiChatRoleService;
24 import cn.iocoder.yudao.module.ai.service.AiChatService; 27 import cn.iocoder.yudao.module.ai.service.AiChatService;
  28 +import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
25 import lombok.AllArgsConstructor; 29 import lombok.AllArgsConstructor;
26 import lombok.extern.slf4j.Slf4j; 30 import lombok.extern.slf4j.Slf4j;
  31 +import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
27 import org.springframework.stereotype.Service; 32 import org.springframework.stereotype.Service;
28 import org.springframework.transaction.annotation.Transactional; 33 import org.springframework.transaction.annotation.Transactional;
29 import reactor.core.publisher.Flux; 34 import reactor.core.publisher.Flux;
30 35
31 import java.util.List; 36 import java.util.List;
  37 +import java.util.Map;
  38 +import java.util.Set;
32 import java.util.concurrent.atomic.AtomicInteger; 39 import java.util.concurrent.atomic.AtomicInteger;
33 import java.util.function.Consumer; 40 import java.util.function.Consumer;
  41 +import java.util.stream.Collectors;
34 42
35 /** 43 /**
36 * 聊天 service 44 * 聊天 service
@@ -50,6 +58,7 @@ public class AiChatServiceImpl implements AiChatService { @@ -50,6 +58,7 @@ public class AiChatServiceImpl implements AiChatService {
50 private final AiChatConversationService chatConversationService; 58 private final AiChatConversationService chatConversationService;
51 private final AiChatModelService aiChatModalService; 59 private final AiChatModelService aiChatModalService;
52 private final AiChatRoleService aiChatRoleService; 60 private final AiChatRoleService aiChatRoleService;
  61 + private final HttpMessageConverters messageConverters;
53 62
54 @Transactional(rollbackFor = Exception.class) 63 @Transactional(rollbackFor = Exception.class)
55 public AiChatMessageRespVO chat(AiChatMessageSendReqVO req) { 64 public AiChatMessageRespVO chat(AiChatMessageSendReqVO req) {
@@ -121,10 +130,15 @@ public class AiChatServiceImpl implements AiChatService { @@ -121,10 +130,15 @@ public class AiChatServiceImpl implements AiChatService {
121 return insertChatMessageDO; 130 return insertChatMessageDO;
122 } 131 }
123 132
124 - public Flux<AiChatMessageRespVO> chatStream(AiChatMessageSendReqVO req) { 133 + public Flux<AiChatMessageRespVO> chatStream(AiChatMessageSendStreamReqVO req) {
125 Long loginUserId = SecurityFrameworkUtils.getLoginUserId(); 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 + }
126 // 查询对话 140 // 查询对话
127 - AiChatConversationRespVO conversation = chatConversationService.getConversationOfValidate(req.getConversationId()); 141 + AiChatConversationRespVO conversation = chatConversationService.getConversationOfValidate(aiChatMessageDO.getConversationId());
128 // 获取对话模型 142 // 获取对话模型
129 AiChatModelDO chatModel = aiChatModalService.validateChatModel(conversation.getModelId()); 143 AiChatModelDO chatModel = aiChatModalService.validateChatModel(conversation.getModelId());
130 // 获取角色信息 144 // 获取角色信息
@@ -135,14 +149,14 @@ public class AiChatServiceImpl implements AiChatService { @@ -135,14 +149,14 @@ public class AiChatServiceImpl implements AiChatService {
135 // 校验角色是否公开 149 // 校验角色是否公开
136 aiChatRoleService.validateIsPublic(aiChatRoleDO); 150 aiChatRoleService.validateIsPublic(aiChatRoleDO);
137 // 创建 chat 需要的 Prompt 151 // 创建 chat 需要的 Prompt
138 - 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());
139 // req.setTopK(req.getTopK()); 157 // req.setTopK(req.getTopK());
140 // req.setTopP(req.getTopP()); 158 // req.setTopP(req.getTopP());
141 // req.setTemperature(req.getTemperature()); 159 // req.setTemperature(req.getTemperature());
142 - // 保存 chat message  
143 - insertChatMessage(conversation.getId(), MessageType.USER, loginUserId, conversation.getRoleId(),  
144 - chatModel.getModel(), chatModel.getId(), req.getContent(),  
145 - null, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts());  
146 // 获取 client 类型 160 // 获取 client 类型
147 AiPlatformEnum platformEnum = AiPlatformEnum.validatePlatform(chatModel.getPlatform()); 161 AiPlatformEnum platformEnum = AiPlatformEnum.validatePlatform(chatModel.getPlatform());
148 StreamingChatClient streamingChatClient = aiChatClientFactory.getStreamingChatClient(platformEnum); 162 StreamingChatClient streamingChatClient = aiChatClientFactory.getStreamingChatClient(platformEnum);
@@ -151,7 +165,8 @@ public class AiChatServiceImpl implements AiChatService { @@ -151,7 +165,8 @@ public class AiChatServiceImpl implements AiChatService {
151 StringBuffer contentBuffer = new StringBuffer(); 165 StringBuffer contentBuffer = new StringBuffer();
152 AtomicInteger tokens = new AtomicInteger(0); 166 AtomicInteger tokens = new AtomicInteger(0);
153 return streamResponse.map(res -> { 167 return streamResponse.map(res -> {
154 - AiChatMessageRespVO aiChatMessageRespVO = new AiChatMessageRespVO(); 168 + AiChatMessageRespVO aiChatMessageRespVO =
  169 + AiChatMessageConvert.INSTANCE.convertAiChatMessageRespVO(systemMessage);
155 aiChatMessageRespVO.setContent(res.getResult().getOutput().getContent()); 170 aiChatMessageRespVO.setContent(res.getResult().getOutput().getContent());
156 contentBuffer.append(res.getResult().getOutput().getContent()); 171 contentBuffer.append(res.getResult().getOutput().getContent());
157 tokens.incrementAndGet(); 172 tokens.incrementAndGet();
@@ -162,34 +177,71 @@ public class AiChatServiceImpl implements AiChatService { @@ -162,34 +177,71 @@ public class AiChatServiceImpl implements AiChatService {
162 public void run() { 177 public void run() {
163 log.info("发送完成!"); 178 log.info("发送完成!");
164 // 保存 chat message 179 // 保存 chat message
165 - insertChatMessage(conversation.getId(), MessageType.SYSTEM, loginUserId, conversation.getRoleId(),  
166 - chatModel.getModel(), chatModel.getId(), contentBuffer.toString(),  
167 - 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 + );
168 } 185 }
169 }).doOnError(new Consumer<Throwable>() { 186 }).doOnError(new Consumer<Throwable>() {
170 @Override 187 @Override
171 public void accept(Throwable throwable) { 188 public void accept(Throwable throwable) {
172 log.error("发送错误 {}!", throwable.getMessage()); 189 log.error("发送错误 {}!", throwable.getMessage());
173 - // 保存 chat message  
174 - insertChatMessage(conversation.getId(), MessageType.SYSTEM, loginUserId, conversation.getRoleId(),  
175 - chatModel.getModel(), chatModel.getId(), throwable.getMessage(),  
176 - 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 + );
177 } 196 }
178 }); 197 });
179 } 198 }
180 199
181 @Override 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
182 public List<AiChatMessageRespVO> getMessageListByConversationId(Long conversationId) { 221 public List<AiChatMessageRespVO> getMessageListByConversationId(Long conversationId) {
183 // 校验对话是否存在 222 // 校验对话是否存在
184 chatConversationService.validateExists(conversationId); 223 chatConversationService.validateExists(conversationId);
185 // 获取对话所有 message 224 // 获取对话所有 message
186 List<AiChatMessageDO> aiChatMessageDOList = aiChatMessageMapper.selectByConversationId(conversationId); 225 List<AiChatMessageDO> aiChatMessageDOList = aiChatMessageMapper.selectByConversationId(conversationId);
  226 + // 获取模型信息
  227 + Set<Long> modalIds = aiChatMessageDOList.stream().map(AiChatMessageDO::getModelId).collect(Collectors.toSet());
  228 + List<AiChatModelDO> modalList = aiChatModalService.getModalByIds(modalIds);
  229 + Map<Long, AiChatModelDO> modalIdMap = modalList.stream().collect(Collectors.toMap(AiChatModelDO::getId, o -> o));
187 // 转换 AiChatMessageRespVO 230 // 转换 AiChatMessageRespVO
188 - return AiChatMessageConvert.INSTANCE.convertAiChatMessageRespVOList(aiChatMessageDOList); 231 + List<AiChatMessageRespVO> aiChatMessageRespList = AiChatMessageConvert.INSTANCE.convertAiChatMessageRespVOList(aiChatMessageDOList);
  232 + // 设置用户头像 和 模型头像 todo @芋艿 这里需要转换 用户头像、模型头像
  233 + return aiChatMessageRespList.stream().map(item -> {
  234 + if (modalIdMap.containsKey(item.getModelId())) {
  235 +// modalIdMap.get(item.getModelId());
  236 +// item.setModelImage()
  237 + }
  238 + return item;
  239 + }).collect(Collectors.toList());
189 } 240 }
190 241
191 @Override 242 @Override
192 public Boolean deleteMessage(Long id) { 243 public Boolean deleteMessage(Long id) {
193 return aiChatMessageMapper.deleteById(id) > 0; 244 return aiChatMessageMapper.deleteById(id) > 0;
194 } 245 }
  246 +
195 } 247 }
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelService.java
@@ -8,6 +8,9 @@ import jakarta.validation.Valid; @@ -8,6 +8,9 @@ import jakarta.validation.Valid;
8 8
9 import java.util.List; 9 import java.util.List;
10 10
  11 +import java.util.List;
  12 +import java.util.Set;
  13 +
11 /** 14 /**
12 * AI 聊天模型 Service 接口 15 * AI 聊天模型 Service 接口
13 * 16 *
@@ -70,4 +73,11 @@ public interface AiChatModelService { @@ -70,4 +73,11 @@ public interface AiChatModelService {
70 */ 73 */
71 List<AiChatModelDO> getChatModelList(Integer status); 74 List<AiChatModelDO> getChatModelList(Integer status);
72 75
  76 + /**
  77 + * 获取 - 根据多个 ids 获取
  78 + *
  79 + * @param modalIds
  80 + * @return
  81 + */
  82 + List<AiChatModelDO> getModalByIds(Set<Long> modalIds);
73 } 83 }
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java
@@ -14,6 +14,9 @@ import org.springframework.validation.annotation.Validated; @@ -14,6 +14,9 @@ import org.springframework.validation.annotation.Validated;
14 14
15 import java.util.List; 15 import java.util.List;
16 16
  17 +import java.util.List;
  18 +import java.util.Set;
  19 +
17 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 20 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
18 import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.*; 21 import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.*;
19 22
@@ -97,4 +100,9 @@ public class AiChatModelServiceImpl implements AiChatModelService { @@ -97,4 +100,9 @@ public class AiChatModelServiceImpl implements AiChatModelService {
97 return null; 100 return null;
98 } 101 }
99 102
  103 + @Override
  104 + public List<AiChatModelDO> getModalByIds(Set<Long> modalIds) {
  105 + return chatModelMapper.selectByIds(modalIds);
  106 + }
  107 +
100 } 108 }
101 \ No newline at end of file 109 \ No newline at end of file