Sfoglia il codice sorgente

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

YunaiV 11 mesi fa
parent
commit
d0c7b2333a
13 ha cambiato i file con 67 aggiunte e 119 eliminazioni
  1. 13 9
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatConversationController.java
  2. 3 10
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationRespVO.java
  3. 3 0
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationUpdateMyReqVO.java
  4. 1 4
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java
  5. 2 6
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveMyReqVO.java
  6. 2 6
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveReqVO.java
  7. 0 39
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/convert/AiChatConversationConvert.java
  8. 4 0
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java
  9. 1 5
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java
  10. 9 8
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationService.java
  11. 25 26
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java
  12. 4 4
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatServiceImpl.java
  13. 0 2
      yudao-module-ai/yudao-module-ai-biz/src/main/resources/http/chat-role.http

+ 13 - 9
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatConversationController.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.ai.controller.admin.chat;
 
+import cn.hutool.core.util.ObjUtil;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO;
@@ -49,20 +50,23 @@ public class AiChatConversationController {
         return success(BeanUtils.toBean(list, AiChatConversationRespVO.class));
     }
 
-    // TODO @fan:实现一下
-    @GetMapping("/get")
-    @Operation(summary = "获得聊天会话")
+    @GetMapping("/get-my")
+    @Operation(summary = "获得【我的】聊天会话")
     @Parameter(name = "id", required = true, description = "会话编号", example = "1024")
-    public CommonResult<AiChatConversationRespVO> getConversation(@RequestParam("id") Long id) {
-        return success(chatConversationService.getConversationOfValidate(id));
+    public CommonResult<AiChatConversationRespVO> getChatConversationMy(@RequestParam("id") Long id) {
+        AiChatConversationDO conversation = chatConversationService.getChatConversation(id);
+        if (conversation != null && ObjUtil.notEqual(conversation.getUserId(), getLoginUserId())) {
+            conversation = null;
+        }
+        return success(BeanUtils.toBean(conversation, AiChatConversationRespVO.class));
     }
 
-    // TODO @fan:实现一下
-    @DeleteMapping("/delete")
+    @DeleteMapping("/delete-my")
     @Operation(summary = "删除聊天会话")
     @Parameter(name = "id", required = true, description = "会话编号", example = "1024")
-    public CommonResult<Boolean> deleteConversation(@RequestParam("id") Long id) {
-        return success(chatConversationService.deleteConversation(id));
+    public CommonResult<Boolean> deleteChatConversationMy(@RequestParam("id") Long id) {
+        chatConversationService.deleteChatConversationMy(id, getLoginUserId());
+        return success(true);
     }
 
     // ========== 会话管理 ==========

+ 3 - 10
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 {
     private Long roleId;
 
     @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
-    @Trans(type = TransType.SIMPLE, target = AiChatModelDO.class, fields = {"maxTokens", "maxContexts"},
-            refs = {"modelMaxTokens", "modelMaxContexts"})
     private Long modelId;
 
     @Schema(description = "模型标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "ERNIE-Bot-turbo-0922")
     private String model;
 
+    @Schema(description = "角色设定", example = "一个快乐的程序员")
+    private String systemMessage;
+
     @Schema(description = "温度参数", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.8")
     private Double temperature;
 
@@ -53,12 +54,4 @@ public class AiChatConversationRespVO implements VO {
     @Schema(description = "角色头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
     private String roleAvatar;
 
-    // ========== 关联 model 信息 ==========
-
-    @Schema(description = "模型的单条回复的最大 Token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096")
-    private Integer modelMaxTokens;
-
-    @Schema(description = "模型的上下文的最大 Message 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
-    private Integer modelMaxContexts;
-
 }

+ 3 - 0
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 {
     @Schema(description = "模型编号", example = "1")
     private Long modelId;
 
+    @Schema(description = "角色设定", example = "一个快乐的程序员")
+    private String systemMessage;
+
     @Schema(description = "温度参数", example = "0.8")
     private Double temperature;
 

+ 1 - 4
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 {
     @Schema(description = "角色描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "你说的对")
     private String description;
 
-    @Schema(description = "角色欢迎语", requiredMode = Schema.RequiredMode.REQUIRED)
-    private String welcomeMessage;
-
-    @Schema(description = "角色上下文", requiredMode = Schema.RequiredMode.REQUIRED)
+    @Schema(description = "角色设定", requiredMode = Schema.RequiredMode.REQUIRED)
     private String systemMessage;
 
     @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")

+ 2 - 6
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 {
     @NotEmpty(message = "角色描述不能为空")
     private String description;
 
-    @Schema(description = "角色欢迎语", requiredMode = Schema.RequiredMode.REQUIRED, example = "Talk is cheap, i will show code")
-    @NotEmpty(message = "角色欢迎语不能为空")
-    private String welcomeMessage;
-
-    @Schema(description = "角色上下文", requiredMode = Schema.RequiredMode.REQUIRED, example = "现在开始你扮演一位程序员,你是一名优秀的程序员,具有很强的逻辑思维能力,总能高效的解决问题")
-    @NotEmpty(message = "角色上下文不能为空")
+    @Schema(description = "角色设定", requiredMode = Schema.RequiredMode.REQUIRED, example = "现在开始你扮演一位程序员,你是一名优秀的程序员,具有很强的逻辑思维能力,总能高效的解决问题")
+    @NotEmpty(message = "角色设定不能为空")
     private String systemMessage;
 
 }

+ 2 - 6
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 {
     @NotEmpty(message = "角色描述不能为空")
     private String description;
 
-    @Schema(description = "角色欢迎语", requiredMode = Schema.RequiredMode.REQUIRED, example = "Talk is cheap, i will show code")
-    @NotEmpty(message = "角色欢迎语不能为空")
-    private String welcomeMessage;
-
-    @Schema(description = "角色上下文", requiredMode = Schema.RequiredMode.REQUIRED, example = "现在开始你扮演一位程序员,你是一名优秀的程序员,具有很强的逻辑思维能力,总能高效的解决问题")
-    @NotEmpty(message = "角色上下文不能为空")
+    @Schema(description = "角色设定", requiredMode = Schema.RequiredMode.REQUIRED, example = "现在开始你扮演一位程序员,你是一名优秀的程序员,具有很强的逻辑思维能力,总能高效的解决问题")
+    @NotEmpty(message = "角色设定不能为空")
     private String systemMessage;
 
     @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")

+ 0 - 39
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/convert/AiChatConversationConvert.java

@@ -1,39 +0,0 @@
-package cn.iocoder.yudao.module.ai.convert;
-
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO;
-import org.mapstruct.Mapper;
-import org.mapstruct.factory.Mappers;
-
-import java.util.List;
-
-/**
- * 聊天 对话 convert
- *
- * @author fansili
- * @time 2024/4/18 16:39
- * @since 1.0
- */
-@Mapper
-public interface AiChatConversationConvert {
-
-    AiChatConversationConvert INSTANCE = Mappers.getMapper(AiChatConversationConvert.class);
-
-    /**
-     * 转换 - 多个 ChatConversationRes
-     *
-     * @param top100Conversation
-     * @return
-     */
-    List<AiChatConversationRespVO> covnertChatConversationResList(List<AiChatConversationDO> top100Conversation);
-
-    /**
-     * 转换 - 单个 ChatConversationRes
-     *
-     * @param aiChatConversationDO
-     * @return
-     */
-    AiChatConversationRespVO covnertChatConversationRes(AiChatConversationDO aiChatConversationDO);
-
-}

+ 4 - 0
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java

@@ -81,6 +81,10 @@ public class AiChatConversationDO extends BaseDO {
 
     // ========== 会话配置 ==========
 
+    /**
+     * 角色设定
+     */
+    private String systemMessage;
     /**
      * 温度参数
      *

+ 1 - 5
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 {
      */
     private String description;
     /**
-     * 角色欢迎语
-     */
-    private String welcomeMessage;
-    /**
-     * 角色上下文
+     * 角色设定
      */
     private String systemMessage;
 

+ 9 - 8
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationService.java

@@ -1,7 +1,6 @@
 package cn.iocoder.yudao.module.ai.service.chat;
 
 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO;
 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO;
 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
 
@@ -40,19 +39,20 @@ public interface AiChatConversationService {
     List<AiChatConversationDO> getChatConversationListByUserId(Long userId);
 
     /**
-     * 获取 - 对
+     * 获得聊天会
      *
-     * @param id
-     * @return
+     * @param id 编号
+     * @return 聊天会话
      */
-    AiChatConversationRespVO getConversationOfValidate(Long id);
+    AiChatConversationDO getChatConversation(Long id);
 
     /**
-     * 删除 - 根据id
+     * 删除【我的】聊天会话
      *
-     * @param id
+     * @param id 编号
+     * @param userId 用户编号
      */
-    Boolean deleteConversation(Long id);
+    void deleteChatConversationMy(Long id, Long userId);
 
     /**
      * 校验 - 是否存在
@@ -61,4 +61,5 @@ public interface AiChatConversationService {
      * @return
      */
     AiChatConversationDO validateExists(Long id);
+
 }

+ 25 - 26
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;
 import cn.hutool.core.util.ObjUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO;
 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO;
 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO;
-import cn.iocoder.yudao.module.ai.convert.AiChatConversationConvert;
 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
@@ -59,7 +57,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
         // 2. 创建 AiChatConversationDO 聊天对话
         AiChatConversationDO conversation = new AiChatConversationDO()
                 .setUserId(userId).setTitle(AiChatConversationDO.TITLE_DEFAULT).setPinned(false)
-                .setRoleId(role.getId()).setModelId(model.getId()).setModel(model.getModel())
+                .setRoleId(role.getId()).setModelId(model.getId()).setModel(model.getModel()).setSystemMessage(role.getSystemMessage())
                 .setTemperature(model.getTemperature()).setMaxTokens(model.getMaxTokens()).setMaxContexts(model.getMaxContexts());
         chatConversationMapper.insert(conversation);
         return conversation.getId();
@@ -73,24 +71,19 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
             throw exception(CHAT_CONVERSATION_NOT_EXISTS);
         }
         // 1.2 校验模型是否存在(修改模型的情况)
-        if (!ObjectUtil.isAllEmpty(updateReqVO.getModelId(), updateReqVO.getMaxTokens(), updateReqVO.getMaxContexts())) {
-            AiChatModelDO model = chatModalService.validateChatModel(updateReqVO.getModelId());
-            Assert.notNull(model, "必须找到默认模型");
-            validateChatModel(model);
-            // 校验 Token 数量、上下文数量
-            if (updateReqVO.getMaxTokens() != null && updateReqVO.getMaxTokens() > model.getMaxTokens()) {
-                throw exception(CHAT_CONVERSATION_UPDATE_MAX_TOKENS_ERROR);
-            }
-            if (updateReqVO.getMaxContexts() != null && updateReqVO.getMaxContexts() > model.getMaxContexts()) {
-                throw exception(CHAT_CONVERSATION_UPDATE_MAX_CONTEXTS_ERROR);
-            }
+        AiChatModelDO model = null;
+        if (updateReqVO.getModelId() != null) {
+            model = chatModalService.validateChatModel(updateReqVO.getModelId());
         }
 
-        // 更新对话信息
+        // 2. 更新对话信息
         AiChatConversationDO updateObj = BeanUtils.toBean(updateReqVO, AiChatConversationDO.class);
         if (Boolean.TRUE.equals(updateReqVO.getPinned())) {
             updateObj.setPinnedTime(LocalDateTime.now());
         }
+        if (model != null) {
+            updateObj.setModel(model.getModel());
+        }
         chatConversationMapper.updateById(updateObj);
     }
 
@@ -99,22 +92,28 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
         return chatConversationMapper.selectListByUserId(userId);
     }
 
-    private void validateChatModel(AiChatModelDO model) {
-        if (ObjectUtil.isAllNotEmpty(model.getTemperature(), model.getMaxTokens(), model.getMaxContexts())) {
-            return;
-        }
-        throw exception(CHAT_CONVERSATION_MODEL_ERROR);
+    @Override
+    public AiChatConversationDO getChatConversation(Long id) {
+        return chatConversationMapper.selectById(id);
     }
 
     @Override
-    public AiChatConversationRespVO getConversationOfValidate(Long id) {
-        AiChatConversationDO aiChatConversationDO = validateExists(id);
-        return AiChatConversationConvert.INSTANCE.covnertChatConversationRes(aiChatConversationDO);
+    public void deleteChatConversationMy(Long id, Long userId) {
+        // 1. 校验对话是否存在
+        AiChatConversationDO conversation = validateExists(id);
+        if (ObjUtil.notEqual(conversation.getUserId(), userId)) {
+            throw exception(CHAT_CONVERSATION_NOT_EXISTS);
+        }
+
+        // 2. 执行删除
+        chatConversationMapper.deleteById(id);
     }
 
-    @Override
-    public Boolean deleteConversation(Long id) {
-        return chatConversationMapper.deleteById(id) > 0;
+    private void validateChatModel(AiChatModelDO model) {
+        if (ObjectUtil.isAllNotEmpty(model.getTemperature(), model.getMaxTokens(), model.getMaxContexts())) {
+            return;
+        }
+        throw exception(CHAT_CONVERSATION_MODEL_ERROR);
     }
 
     public AiChatConversationDO validateExists(Long id) {

+ 4 - 4
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;
 
 import cn.hutool.core.exceptions.ExceptionUtil;
 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
+import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
 import org.springframework.ai.chat.ChatClient;
 import org.springframework.ai.chat.ChatResponse;
 import org.springframework.ai.chat.StreamingChatClient;
@@ -54,7 +55,6 @@ public class AiChatServiceImpl implements AiChatService {
     private final AiChatClientFactory aiChatClientFactory;
 
     private final AiChatMessageMapper aiChatMessageMapper;
-    private final AiChatConversationMapper aiChatConversationMapper;
     private final AiChatConversationService chatConversationService;
     private final AiChatModelService aiChatModalService;
     private final AiChatRoleService chatRoleService;
@@ -63,7 +63,7 @@ public class AiChatServiceImpl implements AiChatService {
     public AiChatMessageRespVO chat(AiChatMessageSendReqVO req) {
         Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
         // 查询对话
-        AiChatConversationRespVO conversation = chatConversationService.getConversationOfValidate(req.getConversationId());
+        AiChatConversationDO conversation = chatConversationService.validateExists(req.getConversationId());
         // 获取对话模型
         AiChatModelDO chatModel = aiChatModalService.validateChatModel(conversation.getModelId());
         // 获取角色信息
@@ -130,7 +130,7 @@ public class AiChatServiceImpl implements AiChatService {
             throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_CHAT_MESSAGE_NOT_EXIST);
         }
         // 查询对话
-        AiChatConversationRespVO conversation = chatConversationService.getConversationOfValidate(aiChatMessageDO.getConversationId());
+        AiChatConversationDO conversation = chatConversationService.validateExists(aiChatMessageDO.getConversationId());
         // 获取对话模型
         AiChatModelDO chatModel = aiChatModalService.validateChatModel(conversation.getModelId());
         // 获取角色信息
@@ -188,7 +188,7 @@ public class AiChatServiceImpl implements AiChatService {
     public AiChatMessageRespVO add(AiChatMessageAddReqVO req) {
         Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
         // 查询对话
-        AiChatConversationRespVO conversation = chatConversationService.getConversationOfValidate(req.getConversationId());
+        AiChatConversationDO conversation = chatConversationService.validateExists(req.getConversationId());
         // 获取对话模型
         AiChatModelDO chatModel = aiChatModalService.validateChatModel(conversation.getModelId());
         AiChatMessageDO userMessage = insertChatMessage(conversation.getId(), MessageType.USER, loginUserId, conversation.getRoleId(),

+ 0 - 2
yudao-module-ai/yudao-module-ai-biz/src/main/resources/http/chat-role.http

@@ -14,7 +14,6 @@ Authorization: {{token}}
   "avatar": "http://baidu.com",
   "category": "writing",
   "description": "采用gpt3.5模型,拥有小红书优质作者写作经验。",
-  "welcomeMessage": "欢迎使用小红书写作模型!",
   "systemMessage": "你是一名优秀的小红书人文、风光作者,你热爱旅游,每去往一个城市你都会用美妙的文字抒写着这座城市的大街小巷,描述着这座城市的美好。",
   "publicStatus": 0,
   "sort": 0
@@ -33,7 +32,6 @@ Authorization: {{token}}
   "avatar": "http://baidu.com",
   "category": "writing",
   "description": "采用gpt3.5模型,拥有小红书优质作者写作经验。",
-  "welcomeMessage": "欢迎使用小红书写作模型!",
   "systemMessage": "你是一名优秀的小红书人文、风光作者,你热爱旅游,每去往一个城市你都会用美妙的文字抒写着这座城市的大街小巷,描述着这座城市的美好。",
   "publicStatus": 0,
   "sort": 0,