Commit d0c7b2333a4103c6ac71fc50a3e4d4918f01fc38

Authored by YunaiV
1 parent 78bb2d1d

【新增】AI:聊天对话的更新模型

Showing 13 changed files with 67 additions and 119 deletions
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatConversationController.java
1 1 package cn.iocoder.yudao.module.ai.controller.admin.chat;
2 2  
  3 +import cn.hutool.core.util.ObjUtil;
3 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 6 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO;
... ... @@ -49,20 +50,23 @@ public class AiChatConversationController {
49 50 return success(BeanUtils.toBean(list, AiChatConversationRespVO.class));
50 51 }
51 52  
52   - // TODO @fan:实现一下
53   - @GetMapping("/get")
54   - @Operation(summary = "获得聊天会话")
  53 + @GetMapping("/get-my")
  54 + @Operation(summary = "获得【我的】聊天会话")
55 55 @Parameter(name = "id", required = true, description = "会话编号", example = "1024")
56   - public CommonResult<AiChatConversationRespVO> getConversation(@RequestParam("id") Long id) {
57   - return success(chatConversationService.getConversationOfValidate(id));
  56 + public CommonResult<AiChatConversationRespVO> getChatConversationMy(@RequestParam("id") Long id) {
  57 + AiChatConversationDO conversation = chatConversationService.getChatConversation(id);
  58 + if (conversation != null && ObjUtil.notEqual(conversation.getUserId(), getLoginUserId())) {
  59 + conversation = null;
  60 + }
  61 + return success(BeanUtils.toBean(conversation, AiChatConversationRespVO.class));
58 62 }
59 63  
60   - // TODO @fan:实现一下
61   - @DeleteMapping("/delete")
  64 + @DeleteMapping("/delete-my")
62 65 @Operation(summary = "删除聊天会话")
63 66 @Parameter(name = "id", required = true, description = "会话编号", example = "1024")
64   - public CommonResult<Boolean> deleteConversation(@RequestParam("id") Long id) {
65   - return success(chatConversationService.deleteConversation(id));
  67 + public CommonResult<Boolean> deleteChatConversationMy(@RequestParam("id") Long id) {
  68 + chatConversationService.deleteChatConversationMy(id, getLoginUserId());
  69 + return success(true);
66 70 }
67 71  
68 72 // ========== 会话管理 ==========
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationRespVO.java
... ... @@ -32,13 +32,14 @@ public class AiChatConversationRespVO implements VO {
32 32 private Long roleId;
33 33  
34 34 @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
35   - @Trans(type = TransType.SIMPLE, target = AiChatModelDO.class, fields = {"maxTokens", "maxContexts"},
36   - refs = {"modelMaxTokens", "modelMaxContexts"})
37 35 private Long modelId;
38 36  
39 37 @Schema(description = "模型标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "ERNIE-Bot-turbo-0922")
40 38 private String model;
41 39  
  40 + @Schema(description = "角色设定", example = "一个快乐的程序员")
  41 + private String systemMessage;
  42 +
42 43 @Schema(description = "温度参数", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.8")
43 44 private Double temperature;
44 45  
... ... @@ -53,12 +54,4 @@ public class AiChatConversationRespVO implements VO {
53 54 @Schema(description = "角色头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
54 55 private String roleAvatar;
55 56  
56   - // ========== 关联 model 信息 ==========
57   -
58   - @Schema(description = "模型的单条回复的最大 Token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096")
59   - private Integer modelMaxTokens;
60   -
61   - @Schema(description = "模型的上下文的最大 Message 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
62   - private Integer modelMaxContexts;
63   -
64 57 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationUpdateMyReqVO.java
... ... @@ -21,6 +21,9 @@ public class AiChatConversationUpdateMyReqVO {
21 21 @Schema(description = "模型编号", example = "1")
22 22 private Long modelId;
23 23  
  24 + @Schema(description = "角色设定", example = "一个快乐的程序员")
  25 + private String systemMessage;
  26 +
24 27 @Schema(description = "温度参数", example = "0.8")
25 28 private Double temperature;
26 29  
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java
... ... @@ -42,10 +42,7 @@ public class AiChatRoleRespVO implements VO {
42 42 @Schema(description = "角色描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "你说的对")
43 43 private String description;
44 44  
45   - @Schema(description = "角色欢迎语", requiredMode = Schema.RequiredMode.REQUIRED)
46   - private String welcomeMessage;
47   -
48   - @Schema(description = "角色上下文", requiredMode = Schema.RequiredMode.REQUIRED)
  45 + @Schema(description = "角色设定", requiredMode = Schema.RequiredMode.REQUIRED)
49 46 private String systemMessage;
50 47  
51 48 @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveMyReqVO.java
... ... @@ -25,12 +25,8 @@ public class AiChatRoleSaveMyReqVO {
25 25 @NotEmpty(message = "角色描述不能为空")
26 26 private String description;
27 27  
28   - @Schema(description = "角色欢迎语", requiredMode = Schema.RequiredMode.REQUIRED, example = "Talk is cheap, i will show code")
29   - @NotEmpty(message = "角色欢迎语不能为空")
30   - private String welcomeMessage;
31   -
32   - @Schema(description = "角色上下文", requiredMode = Schema.RequiredMode.REQUIRED, example = "现在开始你扮演一位程序员,你是一名优秀的程序员,具有很强的逻辑思维能力,总能高效的解决问题")
33   - @NotEmpty(message = "角色上下文不能为空")
  28 + @Schema(description = "角色设定", requiredMode = Schema.RequiredMode.REQUIRED, example = "现在开始你扮演一位程序员,你是一名优秀的程序员,具有很强的逻辑思维能力,总能高效的解决问题")
  29 + @NotEmpty(message = "角色设定不能为空")
34 30 private String systemMessage;
35 31  
36 32 }
37 33 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveReqVO.java
... ... @@ -38,12 +38,8 @@ public class AiChatRoleSaveReqVO {
38 38 @NotEmpty(message = "角色描述不能为空")
39 39 private String description;
40 40  
41   - @Schema(description = "角色欢迎语", requiredMode = Schema.RequiredMode.REQUIRED, example = "Talk is cheap, i will show code")
42   - @NotEmpty(message = "角色欢迎语不能为空")
43   - private String welcomeMessage;
44   -
45   - @Schema(description = "角色上下文", requiredMode = Schema.RequiredMode.REQUIRED, example = "现在开始你扮演一位程序员,你是一名优秀的程序员,具有很强的逻辑思维能力,总能高效的解决问题")
46   - @NotEmpty(message = "角色上下文不能为空")
  41 + @Schema(description = "角色设定", requiredMode = Schema.RequiredMode.REQUIRED, example = "现在开始你扮演一位程序员,你是一名优秀的程序员,具有很强的逻辑思维能力,总能高效的解决问题")
  42 + @NotEmpty(message = "角色设定不能为空")
47 43 private String systemMessage;
48 44  
49 45 @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/convert/AiChatConversationConvert.java deleted
1   -package cn.iocoder.yudao.module.ai.convert;
2   -
3   -import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO;
4   -import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
5   -import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO;
6   -import org.mapstruct.Mapper;
7   -import org.mapstruct.factory.Mappers;
8   -
9   -import java.util.List;
10   -
11   -/**
12   - * 聊天 对话 convert
13   - *
14   - * @author fansili
15   - * @time 2024/4/18 16:39
16   - * @since 1.0
17   - */
18   -@Mapper
19   -public interface AiChatConversationConvert {
20   -
21   - AiChatConversationConvert INSTANCE = Mappers.getMapper(AiChatConversationConvert.class);
22   -
23   - /**
24   - * 转换 - 多个 ChatConversationRes
25   - *
26   - * @param top100Conversation
27   - * @return
28   - */
29   - List<AiChatConversationRespVO> covnertChatConversationResList(List<AiChatConversationDO> top100Conversation);
30   -
31   - /**
32   - * 转换 - 单个 ChatConversationRes
33   - *
34   - * @param aiChatConversationDO
35   - * @return
36   - */
37   - AiChatConversationRespVO covnertChatConversationRes(AiChatConversationDO aiChatConversationDO);
38   -
39   -}
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java
... ... @@ -82,6 +82,10 @@ public class AiChatConversationDO extends BaseDO {
82 82 // ========== 会话配置 ==========
83 83  
84 84 /**
  85 + * 角色设定
  86 + */
  87 + private String systemMessage;
  88 + /**
85 89 * 温度参数
86 90 *
87 91 * 用于调整生成回复的随机性和多样性程度:较低的温度值会使输出更收敛于高频词汇,较高的则增加多样性
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java
... ... @@ -42,11 +42,7 @@ public class AiChatRoleDO extends BaseDO {
42 42 */
43 43 private String description;
44 44 /**
45   - * 角色欢迎语
46   - */
47   - private String welcomeMessage;
48   - /**
49   - * 角色上下文
  45 + * 角色设定
50 46 */
51 47 private String systemMessage;
52 48  
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationService.java
1 1 package cn.iocoder.yudao.module.ai.service.chat;
2 2  
3 3 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO;
4   -import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO;
5 4 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO;
6 5 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
7 6  
... ... @@ -40,19 +39,20 @@ public interface AiChatConversationService {
40 39 List<AiChatConversationDO> getChatConversationListByUserId(Long userId);
41 40  
42 41 /**
43   - * 获取 - 对话
  42 + * 获得聊天会话
44 43 *
45   - * @param id
46   - * @return
  44 + * @param id 编号
  45 + * @return 聊天会话
47 46 */
48   - AiChatConversationRespVO getConversationOfValidate(Long id);
  47 + AiChatConversationDO getChatConversation(Long id);
49 48  
50 49 /**
51   - * 删除 - 根据id
  50 + * 删除【我的】聊天会话
52 51 *
53   - * @param id
  52 + * @param id 编号
  53 + * @param userId 用户编号
54 54 */
55   - Boolean deleteConversation(Long id);
  55 + void deleteChatConversationMy(Long id, Long userId);
56 56  
57 57 /**
58 58 * 校验 - 是否存在
... ... @@ -61,4 +61,5 @@ public interface AiChatConversationService {
61 61 * @return
62 62 */
63 63 AiChatConversationDO validateExists(Long id);
  64 +
64 65 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java
... ... @@ -4,11 +4,9 @@ import cn.hutool.core.lang.Assert;
4 4 import cn.hutool.core.util.ObjUtil;
5 5 import cn.hutool.core.util.ObjectUtil;
6 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7   -import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
8 7 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO;
9 8 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO;
10 9 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO;
11   -import cn.iocoder.yudao.module.ai.convert.AiChatConversationConvert;
12 10 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
13 11 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
14 12 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
... ... @@ -59,7 +57,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
59 57 // 2. 创建 AiChatConversationDO 聊天对话
60 58 AiChatConversationDO conversation = new AiChatConversationDO()
61 59 .setUserId(userId).setTitle(AiChatConversationDO.TITLE_DEFAULT).setPinned(false)
62   - .setRoleId(role.getId()).setModelId(model.getId()).setModel(model.getModel())
  60 + .setRoleId(role.getId()).setModelId(model.getId()).setModel(model.getModel()).setSystemMessage(role.getSystemMessage())
63 61 .setTemperature(model.getTemperature()).setMaxTokens(model.getMaxTokens()).setMaxContexts(model.getMaxContexts());
64 62 chatConversationMapper.insert(conversation);
65 63 return conversation.getId();
... ... @@ -73,24 +71,19 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
73 71 throw exception(CHAT_CONVERSATION_NOT_EXISTS);
74 72 }
75 73 // 1.2 校验模型是否存在(修改模型的情况)
76   - if (!ObjectUtil.isAllEmpty(updateReqVO.getModelId(), updateReqVO.getMaxTokens(), updateReqVO.getMaxContexts())) {
77   - AiChatModelDO model = chatModalService.validateChatModel(updateReqVO.getModelId());
78   - Assert.notNull(model, "必须找到默认模型");
79   - validateChatModel(model);
80   - // 校验 Token 数量、上下文数量
81   - if (updateReqVO.getMaxTokens() != null && updateReqVO.getMaxTokens() > model.getMaxTokens()) {
82   - throw exception(CHAT_CONVERSATION_UPDATE_MAX_TOKENS_ERROR);
83   - }
84   - if (updateReqVO.getMaxContexts() != null && updateReqVO.getMaxContexts() > model.getMaxContexts()) {
85   - throw exception(CHAT_CONVERSATION_UPDATE_MAX_CONTEXTS_ERROR);
86   - }
  74 + AiChatModelDO model = null;
  75 + if (updateReqVO.getModelId() != null) {
  76 + model = chatModalService.validateChatModel(updateReqVO.getModelId());
87 77 }
88 78  
89   - // 更新对话信息
  79 + // 2. 更新对话信息
90 80 AiChatConversationDO updateObj = BeanUtils.toBean(updateReqVO, AiChatConversationDO.class);
91 81 if (Boolean.TRUE.equals(updateReqVO.getPinned())) {
92 82 updateObj.setPinnedTime(LocalDateTime.now());
93 83 }
  84 + if (model != null) {
  85 + updateObj.setModel(model.getModel());
  86 + }
94 87 chatConversationMapper.updateById(updateObj);
95 88 }
96 89  
... ... @@ -99,22 +92,28 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
99 92 return chatConversationMapper.selectListByUserId(userId);
100 93 }
101 94  
102   - private void validateChatModel(AiChatModelDO model) {
103   - if (ObjectUtil.isAllNotEmpty(model.getTemperature(), model.getMaxTokens(), model.getMaxContexts())) {
104   - return;
105   - }
106   - throw exception(CHAT_CONVERSATION_MODEL_ERROR);
  95 + @Override
  96 + public AiChatConversationDO getChatConversation(Long id) {
  97 + return chatConversationMapper.selectById(id);
107 98 }
108 99  
109 100 @Override
110   - public AiChatConversationRespVO getConversationOfValidate(Long id) {
111   - AiChatConversationDO aiChatConversationDO = validateExists(id);
112   - return AiChatConversationConvert.INSTANCE.covnertChatConversationRes(aiChatConversationDO);
  101 + public void deleteChatConversationMy(Long id, Long userId) {
  102 + // 1. 校验对话是否存在
  103 + AiChatConversationDO conversation = validateExists(id);
  104 + if (ObjUtil.notEqual(conversation.getUserId(), userId)) {
  105 + throw exception(CHAT_CONVERSATION_NOT_EXISTS);
  106 + }
  107 +
  108 + // 2. 执行删除
  109 + chatConversationMapper.deleteById(id);
113 110 }
114 111  
115   - @Override
116   - public Boolean deleteConversation(Long id) {
117   - return chatConversationMapper.deleteById(id) > 0;
  112 + private void validateChatModel(AiChatModelDO model) {
  113 + if (ObjectUtil.isAllNotEmpty(model.getTemperature(), model.getMaxTokens(), model.getMaxContexts())) {
  114 + return;
  115 + }
  116 + throw exception(CHAT_CONVERSATION_MODEL_ERROR);
118 117 }
119 118  
120 119 public AiChatConversationDO validateExists(Long id) {
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatServiceImpl.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.ai.service.impl;
2 2  
3 3 import cn.hutool.core.exceptions.ExceptionUtil;
4 4 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
  5 +import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
5 6 import org.springframework.ai.chat.ChatClient;
6 7 import org.springframework.ai.chat.ChatResponse;
7 8 import org.springframework.ai.chat.StreamingChatClient;
... ... @@ -54,7 +55,6 @@ public class AiChatServiceImpl implements AiChatService {
54 55 private final AiChatClientFactory aiChatClientFactory;
55 56  
56 57 private final AiChatMessageMapper aiChatMessageMapper;
57   - private final AiChatConversationMapper aiChatConversationMapper;
58 58 private final AiChatConversationService chatConversationService;
59 59 private final AiChatModelService aiChatModalService;
60 60 private final AiChatRoleService chatRoleService;
... ... @@ -63,7 +63,7 @@ public class AiChatServiceImpl implements AiChatService {
63 63 public AiChatMessageRespVO chat(AiChatMessageSendReqVO req) {
64 64 Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
65 65 // 查询对话
66   - AiChatConversationRespVO conversation = chatConversationService.getConversationOfValidate(req.getConversationId());
  66 + AiChatConversationDO conversation = chatConversationService.validateExists(req.getConversationId());
67 67 // 获取对话模型
68 68 AiChatModelDO chatModel = aiChatModalService.validateChatModel(conversation.getModelId());
69 69 // 获取角色信息
... ... @@ -130,7 +130,7 @@ public class AiChatServiceImpl implements AiChatService {
130 130 throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_CHAT_MESSAGE_NOT_EXIST);
131 131 }
132 132 // 查询对话
133   - AiChatConversationRespVO conversation = chatConversationService.getConversationOfValidate(aiChatMessageDO.getConversationId());
  133 + AiChatConversationDO conversation = chatConversationService.validateExists(aiChatMessageDO.getConversationId());
134 134 // 获取对话模型
135 135 AiChatModelDO chatModel = aiChatModalService.validateChatModel(conversation.getModelId());
136 136 // 获取角色信息
... ... @@ -188,7 +188,7 @@ public class AiChatServiceImpl implements AiChatService {
188 188 public AiChatMessageRespVO add(AiChatMessageAddReqVO req) {
189 189 Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
190 190 // 查询对话
191   - AiChatConversationRespVO conversation = chatConversationService.getConversationOfValidate(req.getConversationId());
  191 + AiChatConversationDO conversation = chatConversationService.validateExists(req.getConversationId());
192 192 // 获取对话模型
193 193 AiChatModelDO chatModel = aiChatModalService.validateChatModel(conversation.getModelId());
194 194 AiChatMessageDO userMessage = insertChatMessage(conversation.getId(), MessageType.USER, loginUserId, conversation.getRoleId(),
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/resources/http/chat-role.http
... ... @@ -14,7 +14,6 @@ Authorization: {{token}}
14 14 "avatar": "http://baidu.com",
15 15 "category": "writing",
16 16 "description": "采用gpt3.5模型,拥有小红书优质作者写作经验。",
17   - "welcomeMessage": "欢迎使用小红书写作模型!",
18 17 "systemMessage": "你是一名优秀的小红书人文、风光作者,你热爱旅游,每去往一个城市你都会用美妙的文字抒写着这座城市的大街小巷,描述着这座城市的美好。",
19 18 "publicStatus": 0,
20 19 "sort": 0
... ... @@ -33,7 +32,6 @@ Authorization: {{token}}
33 32 "avatar": "http://baidu.com",
34 33 "category": "writing",
35 34 "description": "采用gpt3.5模型,拥有小红书优质作者写作经验。",
36   - "welcomeMessage": "欢迎使用小红书写作模型!",
37 35 "systemMessage": "你是一名优秀的小红书人文、风光作者,你热爱旅游,每去往一个城市你都会用美妙的文字抒写着这座城市的大街小巷,描述着这座城市的美好。",
38 36 "publicStatus": 0,
39 37 "sort": 0,
... ...