Commit 424210066ff3ce6f5a37d4d6da939d31cdd3eb52

Authored by cherishsince
1 parent f86e24bb

【调整】调整AI聊天模块

yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java
... ... @@ -30,6 +30,7 @@ public interface ErrorCodeConstants {
30 30 // role
31 31  
32 32 ErrorCode AI_CHAT_ROLE_NOT_EXIST = new ErrorCode(1_022_000_060, "chatRole 不存在!");
  33 + ErrorCode AI_CHAT_ROLE_NOT_PUBLIC = new ErrorCode(1_022_000_060, "AI 角色未公开!");
33 34  
34 35 // modal
35 36  
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java
... ... @@ -32,7 +32,7 @@ public class AiChatMessageController {
32 32 @PostMapping("/send")
33 33 public CommonResult<AiChatMessageRespVO> sendMessage(@Validated @ModelAttribute AiChatMessageSendReqVO sendReqVO) {
34 34 // TODO @fan:使用 static import;这样就 success 就行了;
35   - return success(null);
  35 + return success(chatService.chat(sendReqVO));
36 36 }
37 37  
38 38 // TODO @芋艿:调用这个方法异常,Unable to handle the Spring Security Exception because the response is already committed.;可以再试试
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationRespVO.java
... ... @@ -21,6 +21,9 @@ public class AiChatConversationRespVO {
21 21 @Schema(description = "是否置顶", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
22 22 private Boolean pinned;
23 23  
  24 + @Schema(description = "角色编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1")
  25 + private Long roleId;
  26 +
24 27 @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
25 28 private Long modelId;
26 29  
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiChatModalService.java
... ... @@ -52,4 +52,11 @@ public interface AiChatModalService {
52 52 * @return
53 53 */
54 54 AiChatModalRes getChatModalOfValidate(Long modalId);
  55 +
  56 + /**
  57 + * 校验 - 校验是否可用
  58 + *
  59 + * @param chatModal
  60 + */
  61 + void validateAvailable(AiChatModalRes chatModal);
55 62 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiChatRoleService.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.ai.service;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.module.ai.controller.admin.model.vo.role.*;
  5 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
5 6  
6 7 /**
7 8 * chat 角色
... ... @@ -58,4 +59,19 @@ public interface AiChatRoleService {
58 59 * @return
59 60 */
60 61 AiChatRoleRes getChatRole(Long roleId);
  62 +
  63 + /**
  64 + * 校验 - 角色是否存在
  65 + *
  66 + * @param id
  67 + * @return
  68 + */
  69 + AiChatRoleDO validateExists(Long id);
  70 +
  71 + /**
  72 + * 校验 - 角色是否公开
  73 + *
  74 + * @param aiChatRoleDO
  75 + */
  76 + void validateIsPublic(AiChatRoleDO aiChatRoleDO);
61 77 }
... ...
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 3 import cn.iocoder.yudao.module.ai.controller.Utf8SseEmitter;
  4 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;
4 5 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
5 6  
6 7 /**
... ... @@ -15,10 +16,10 @@ public interface AiChatService {
15 16 /**
16 17 * chat
17 18 *
18   - * @param req
  19 + * @param sendReqVO
19 20 * @return
20 21 */
21   - String chat(AiChatMessageSendReqVO req);
  22 + AiChatMessageRespVO chat(AiChatMessageSendReqVO sendReqVO);
22 23  
23 24 /**
24 25 * chat stream
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatConversationServiceImpl.java
... ... @@ -15,7 +15,6 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
15 15 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModalDO;
16 16 import cn.iocoder.yudao.module.ai.dal.mysql.AiChatConversationMapper;
17 17 import cn.iocoder.yudao.module.ai.dal.mysql.AiChatModalMapper;
18   -import cn.iocoder.yudao.module.ai.enums.AiChatModalDisableEnum;
19 18 import cn.iocoder.yudao.module.ai.service.AiChatConversationService;
20 19 import cn.iocoder.yudao.module.ai.service.AiChatModalService;
21 20 import cn.iocoder.yudao.module.ai.service.AiChatRoleService;
... ... @@ -91,9 +90,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
91 90 // 获取模型信息并验证
92 91 AiChatModalRes chatModal = aiChatModalService.getChatModalOfValidate(updateReqVO.getModelId());
93 92 // 校验modal是否可用
94   - if (AiChatModalDisableEnum.YES.getValue().equals(chatModal.getDisable())) {
95   - throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MODAL_DISABLE_NOT_USED);
96   - }
  93 + aiChatModalService.validateAvailable(chatModal);
97 94 // 更新对话信息
98 95 AiChatConversationDO updateAiChatConversationDO
99 96 = AiChatConversationConvert.INSTANCE.convertAiChatConversationDO(updateReqVO);
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatModalServiceImpl.java
... ... @@ -116,6 +116,14 @@ public class AiChatModalServiceImpl implements AiChatModalService {
116 116 return AiChatModalConvert.INSTANCE.convertAiChatModalRes(aiChatModalDO);
117 117 }
118 118  
  119 + @Override
  120 + public void validateAvailable(AiChatModalRes chatModal) {
  121 + // 对话模型是否可用
  122 + if (AiChatModalDisableEnum.YES.getValue().equals(chatModal.getDisable())) {
  123 + throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MODAL_DISABLE_NOT_USED);
  124 + }
  125 + }
  126 +
119 127 private AiChatModalDO validateChatModalExists(Long id) {
120 128 AiChatModalDO aiChatModalDO = aiChatModalMapper.selectById(id);
121 129 if (aiChatModalDO == null) {
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatRoleServiceImpl.java
... ... @@ -70,7 +70,7 @@ public class AiChatRoleServiceImpl implements AiChatRoleService {
70 70 AiChatRoleClassifyEnum.valueOfClassify(req.getClassify());
71 71 AiChatRoleEnableEnum.valueOfType(req.getEnable());
72 72 // 检查角色是否存在
73   - validateChatRoleExists(id);
  73 + validateExists(id);
74 74 // 转换do
75 75 AiChatRoleDO updateChatRole = AiChatRoleConvert.INSTANCE.convertAiChatRoleDO(req);
76 76 updateChatRole.setId(id);
... ... @@ -83,7 +83,7 @@ public class AiChatRoleServiceImpl implements AiChatRoleService {
83 83 // 转换enum,并校验enum
84 84 AiChatRoleEnableEnum.valueOfType(req.getEnable());
85 85 // 检查角色是否存在
86   - validateChatRoleExists(id);
  86 + validateExists(id);
87 87 // 更新
88 88 aiChatRoleMapper.updateById(new AiChatRoleDO()
89 89 .setId(id)
... ... @@ -94,7 +94,7 @@ public class AiChatRoleServiceImpl implements AiChatRoleService {
94 94 @Override
95 95 public void delete(Long chatRoleId) {
96 96 // 检查角色是否存在
97   - validateChatRoleExists(chatRoleId);
  97 + validateExists(chatRoleId);
98 98 // 删除
99 99 aiChatRoleMapper.deleteById(chatRoleId);
100 100 }
... ... @@ -102,15 +102,25 @@ public class AiChatRoleServiceImpl implements AiChatRoleService {
102 102 @Override
103 103 public AiChatRoleRes getChatRole(Long roleId) {
104 104 // 检查角色是否存在
105   - AiChatRoleDO aiChatRoleDO = validateChatRoleExists(roleId);
  105 + AiChatRoleDO aiChatRoleDO = validateExists(roleId);
106 106 return AiChatRoleConvert.INSTANCE.convertAiChatRoleRes(aiChatRoleDO);
107 107 }
108 108  
109   - private AiChatRoleDO validateChatRoleExists(Long id) {
  109 + public AiChatRoleDO validateExists(Long id) {
110 110 AiChatRoleDO aiChatRoleDO = aiChatRoleMapper.selectById(id);
111 111 if (aiChatRoleDO == null) {
112 112 throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_CHAT_ROLE_NOT_EXIST);
113 113 }
114 114 return aiChatRoleDO;
115 115 }
  116 +
  117 + public void validateIsPublic(AiChatRoleDO aiChatRoleDO) {
  118 + if (aiChatRoleDO == null) {
  119 + return;
  120 + }
  121 + if (!aiChatRoleDO.getPublicStatus()) {
  122 + throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_CHAT_ROLE_NOT_PUBLIC);
  123 + }
  124 + }
116 125 }
  126 +
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatServiceImpl.java
... ... @@ -10,14 +10,19 @@ import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt;
10 10 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
11 11 import cn.iocoder.yudao.module.ai.config.AiChatClientFactory;
12 12 import cn.iocoder.yudao.module.ai.controller.Utf8SseEmitter;
  13 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO;
  14 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;
  15 +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
  16 +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiChatModalRes;
13 17 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
  18 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
14 19 import cn.iocoder.yudao.module.ai.dal.mysql.AiChatConversationMapper;
15 20 import cn.iocoder.yudao.module.ai.dal.mysql.AiChatMessageMapper;
16 21 import cn.iocoder.yudao.module.ai.dal.mysql.AiChatRoleMapper;
17 22 import cn.iocoder.yudao.module.ai.service.AiChatConversationService;
  23 +import cn.iocoder.yudao.module.ai.service.AiChatModalService;
  24 +import cn.iocoder.yudao.module.ai.service.AiChatRoleService;
18 25 import cn.iocoder.yudao.module.ai.service.AiChatService;
19   -import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO;
20   -import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
21 26 import lombok.AllArgsConstructor;
22 27 import lombok.extern.slf4j.Slf4j;
23 28 import org.springframework.http.MediaType;
... ... @@ -45,29 +50,39 @@ public class AiChatServiceImpl implements AiChatService {
45 50 private final AiChatMessageMapper aiChatMessageMapper;
46 51 private final AiChatConversationMapper aiChatConversationMapper;
47 52 private final AiChatConversationService chatConversationService;
  53 + private final AiChatModalService aiChatModalService;
  54 + private final AiChatRoleService aiChatRoleService;
48 55  
49   - /**
50   - * chat
51   - *
52   - * @param req
53   - * @return
54   - */
55 56 @Transactional(rollbackFor = Exception.class)
56   - public String chat(AiChatMessageSendReqVO req) {
  57 + public AiChatMessageRespVO chat(AiChatMessageSendReqVO req) {
57 58 Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
  59 + // 查询对话
  60 + AiChatConversationRespVO conversation = chatConversationService.getConversationOfValidate(req.getConversationId());
  61 + // 获取对话模型
  62 + AiChatModalRes chatModal = aiChatModalService.getChatModalOfValidate(conversation.getModelId());
  63 + // 对话模型是否可用
  64 + aiChatModalService.validateAvailable(chatModal);
  65 + // 获取角色信息
  66 + AiChatRoleDO aiChatRoleDO = null;
  67 + if (conversation.getRoleId() != null) {
  68 + aiChatRoleDO = aiChatRoleService.validateExists(conversation.getRoleId());
  69 + }
  70 + // 校验角色是否公开
  71 + aiChatRoleService.validateIsPublic(aiChatRoleDO);
58 72 // 获取 client 类型
59   - AiPlatformEnum platformEnum = AiPlatformEnum.valueOfPlatform(req.getModal());
60   - // 获取对话信息
61   - AiChatConversationRespVO conversationRes = chatConversationService.getConversationOfValidate(req.getConversationId());
  73 + AiPlatformEnum platformEnum = AiPlatformEnum.valueOfPlatform(chatModal.getModal());
62 74 // 保存 chat message
63   - saveChatMessage(req, conversationRes, loginUserId);
  75 + insertChatMessage(conversation.getId(), MessageType.USER, loginUserId, conversation.getRoleId(),
  76 + chatModal.getModal(), chatModal.getId(), req.getContent(),
  77 + null, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts());
64 78 String content = null;
65 79 try {
66 80 // 创建 chat 需要的 Prompt
67   - Prompt prompt = new Prompt(req.getPrompt());
68   - req.setTopK(req.getTopK());
69   - req.setTopP(req.getTopP());
70   - req.setTemperature(req.getTemperature());
  81 + Prompt prompt = new Prompt(req.getContent());
  82 + // TODO @芋艿 @范 看要不要支持这些
  83 +// req.setTopK(req.getTopK());
  84 +// req.setTopP(req.getTopP());
  85 +// req.setTemperature(req.getTemperature());
71 86 // 发送 call 调用
72 87 ChatClient chatClient = aiChatClientFactory.getChatClient(platformEnum);
73 88 ChatResponse call = chatClient.call(prompt);
... ... @@ -78,69 +93,66 @@ public class AiChatServiceImpl implements AiChatService {
78 93 content = ExceptionUtil.getMessage(e);
79 94 } finally {
80 95 // 保存 chat message
81   - saveSystemChatMessage(req, conversationRes, loginUserId, content);
  96 + insertChatMessage(conversation.getId(), MessageType.SYSTEM, loginUserId, conversation.getRoleId(),
  97 + chatModal.getModal(), chatModal.getId(), req.getContent(),
  98 + null, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts());
82 99 }
83   - return content;
  100 + return new AiChatMessageRespVO().setContent(content);
84 101 }
85 102  
86   - private void saveChatMessage(AiChatMessageSendReqVO req, AiChatConversationRespVO conversationRes, Long loginUserId) {
87   - Long chatConversationId = conversationRes.getId();
88   - // 增加 chat message 记录
89   - aiChatMessageMapper.insert(
90   - new AiChatMessageDO()
91   - .setId(null)
92   - .setConversationId(chatConversationId)
93   - .setUserId(loginUserId)
94   - .setMessage(req.getPrompt())
95   - .setMessageType(MessageType.USER.getValue())
96   - .setTopK(req.getTopK())
97   - .setTopP(req.getTopP())
98   - .setTemperature(req.getTemperature())
99   - );
100   - // chat count 先+1
101   - aiChatConversationMapper.updateIncrChatCount(req.getConversationId());
102   - }
  103 + private AiChatMessageDO insertChatMessage(Long conversationId, MessageType messageType, Long loginUserId, Long roleId,
  104 + String model, Long modelId, String content, Integer tokens, Double temperature,
  105 + Integer maxTokens, Integer maxContexts) {
  106 + AiChatMessageDO insertChatMessageDO = new AiChatMessageDO()
  107 + .setId(null)
  108 + .setConversationId(conversationId)
  109 + .setType(messageType.getValue())
  110 + .setUserId(loginUserId)
  111 + .setRoleId(roleId)
  112 + .setModel(model)
  113 + .setModelId(modelId)
  114 + .setContent(content)
  115 + .setTokens(tokens)
103 116  
104   - public void saveSystemChatMessage(AiChatMessageSendReqVO req, AiChatConversationRespVO conversationRes, Long loginUserId, String systemPrompts) {
105   - Long chatConversationId = conversationRes.getId();
  117 + .setTemperature(temperature)
  118 + .setMaxTokens(maxTokens)
  119 + .setMaxContexts(maxContexts);
106 120 // 增加 chat message 记录
107   - aiChatMessageMapper.insert(
108   - new AiChatMessageDO()
109   - .setId(null)
110   - .setConversationId(chatConversationId)
111   - .setUserId(loginUserId)
112   - .setMessage(systemPrompts)
113   - .setMessageType(MessageType.SYSTEM.getValue())
114   - .setTopK(req.getTopK())
115   - .setTopP(req.getTopP())
116   - .setTemperature(req.getTemperature())
117   - );
118   -
  121 + aiChatMessageMapper.insert(insertChatMessageDO);
119 122 // chat count 先+1
120   - aiChatConversationMapper.updateIncrChatCount(req.getConversationId());
  123 + aiChatConversationMapper.updateIncrChatCount(conversationId);
  124 + return insertChatMessageDO;
121 125 }
122 126  
123   - /**
124   - * chat stream
125   - *
126   - * @param req
127   - * @param sseEmitter
128   - * @return
129   - */
130 127 @Override
131 128 public void chatStream(AiChatMessageSendReqVO req, Utf8SseEmitter sseEmitter) {
132 129 Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
133   - // 获取 client 类型
134   - AiPlatformEnum platformEnum = AiPlatformEnum.valueOfPlatform(req.getModal());
135   - // 获取对话信息
136   - AiChatConversationRespVO conversationRes = chatConversationService.getConversationOfValidate(req.getConversationId());
  130 + // 查询对话
  131 + AiChatConversationRespVO conversation = chatConversationService.getConversationOfValidate(req.getConversationId());
  132 + // 获取对话模型
  133 + AiChatModalRes chatModal = aiChatModalService.getChatModalOfValidate(conversation.getModelId());
  134 + // 对话模型是否可用
  135 + aiChatModalService.validateAvailable(chatModal);
  136 + // 获取角色信息
  137 + AiChatRoleDO aiChatRoleDO = null;
  138 + if (conversation.getRoleId() != null) {
  139 + aiChatRoleDO = aiChatRoleService.validateExists(conversation.getRoleId());
  140 + }
  141 + // 校验角色是否公开
  142 + aiChatRoleService.validateIsPublic(aiChatRoleDO);
137 143 // 创建 chat 需要的 Prompt
138   - Prompt prompt = new Prompt(req.getPrompt());
139   - req.setTopK(req.getTopK());
140   - req.setTopP(req.getTopP());
141   - req.setTemperature(req.getTemperature());
  144 + Prompt prompt = new Prompt(req.getContent());
  145 +// req.setTopK(req.getTopK());
  146 +// req.setTopP(req.getTopP());
  147 +// req.setTemperature(req.getTemperature());
142 148 // 保存 chat message
143   - saveChatMessage(req, conversationRes, loginUserId);
  149 + // 保存 chat message
  150 + insertChatMessage(conversation.getId(), MessageType.USER, loginUserId, conversation.getRoleId(),
  151 + chatModal.getModal(), chatModal.getId(), req.getContent(),
  152 + null, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts());
  153 +
  154 + // 获取 client 类型
  155 + AiPlatformEnum platformEnum = AiPlatformEnum.valueOfPlatform(chatModal.getModal());
144 156 StreamingChatClient streamingChatClient = aiChatClientFactory.getStreamingChatClient(platformEnum);
145 157 Flux<ChatResponse> streamResponse = streamingChatClient.stream(prompt);
146 158  
... ... @@ -168,7 +180,10 @@ public class AiChatServiceImpl implements AiChatService {
168 180 log.info("发送完成!");
169 181 sseEmitter.complete();
170 182 // 保存 chat message
171   - saveSystemChatMessage(req, conversationRes, loginUserId, contentBuffer.toString());
  183 + insertChatMessage(conversation.getId(), MessageType.SYSTEM, loginUserId, conversation.getRoleId(),
  184 + chatModal.getModal(), chatModal.getId(), req.getContent(),
  185 + null, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts());
  186 +
172 187 }
173 188 );
174 189 }
... ...