From 4fddec5f0217b3aa4895f136a2066134942a3b10 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Fri, 24 May 2024 20:56:21 +0800 Subject: [PATCH] 【优化】AI:调整 image 相关的包结构 --- yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java | 2 +- yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.java | 2 +- yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatModelController.java | 3 +-- yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatModelMapper.java | 53 ----------------------------------------------------- yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatRoleMapper.java | 50 -------------------------------------------------- yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiImageMapper.java | 29 ----------------------------- yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/image/AiImageMapper.java | 30 ++++++++++++++++++++++++++++++ yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatModelMapper.java | 43 +++++++++++++++++++++++++++++++++++++++++++ yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatRoleMapper.java | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalChatConfigVO.java | 35 ----------------------------------- yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalConfigVO.java | 31 ------------------------------- yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalDallConfigVO.java | 41 ----------------------------------------- yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalMidjourneyConfigVO.java | 16 ---------------- yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/typeHandler/AiChatModelConfigTypeHandler.java | 75 --------------------------------------------------------------------------- yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiImageService.java | 54 ------------------------------------------------------ yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageService.java | 2 +- yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java | 12 +++++++----- yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageService.java | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/YuDaoMidjourneyMessageHandler.java | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/typeHandler/AiChatModelConfigTypeHandler.java | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalChatConfigVO.java | 35 +++++++++++++++++++++++++++++++++++ yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalConfigVO.java | 31 +++++++++++++++++++++++++++++++ yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalDallConfigVO.java | 41 +++++++++++++++++++++++++++++++++++++++++ yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalMidjourneyConfigVO.java | 16 ++++++++++++++++ yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiImageServiceImpl.java | 231 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/midjourneyHandler/YuDaoMidjourneyMessageHandler.java | 126 ------------------------------------------------------------------------------------------------------------------------------ yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelService.java | 11 ++++++----- yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java | 11 +++++------ yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleServiceImpl.java | 2 +- yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiClientFactoryImpl.java | 36 ++++++++++++++---------------------- yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/tongyi/QianWenChatClient.java | 1 + 32 files changed, 769 insertions(+), 785 deletions(-) delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatModelMapper.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatRoleMapper.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiImageMapper.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/image/AiImageMapper.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatModelMapper.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatRoleMapper.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalChatConfigVO.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalConfigVO.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalDallConfigVO.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalMidjourneyConfigVO.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/typeHandler/AiChatModelConfigTypeHandler.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiImageService.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageService.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/YuDaoMidjourneyMessageHandler.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/typeHandler/AiChatModelConfigTypeHandler.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalChatConfigVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalConfigVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalDallConfigVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalMidjourneyConfigVO.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiImageServiceImpl.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/midjourneyHandler/YuDaoMidjourneyMessageHandler.java diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java index 8a2dba0..7211f06 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java @@ -89,7 +89,7 @@ public class AiChatMessageController { @DeleteMapping("/delete") @Parameter(name = "id", required = true, description = "消息编号", example = "1024") public CommonResult deleteChatMessage(@RequestParam("id") Long id) { - chatMessageService.deleteMessage(id, getLoginUserId()); + chatMessageService.deleteChatMessage(id, getLoginUserId()); return success(true); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.java index 7df2592..fd9ea08 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.image; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.ai.controller.admin.image.vo.*; -import cn.iocoder.yudao.module.ai.service.AiImageService; +import cn.iocoder.yudao.module.ai.service.image.AiImageService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.AllArgsConstructor; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatModelController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatModelController.java index 1b50be7..08a53b2 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatModelController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatModelController.java @@ -18,7 +18,6 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.util.List; -import java.util.function.Function; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; @@ -77,7 +76,7 @@ public class AiChatModelController { @Operation(summary = "获得聊天模型列表") @Parameter(name = "status", description = "状态", required = true, example = "1") public CommonResult> getChatModelSimpleList(@RequestParam("status") Integer status) { - List list = chatModelService.getChatModelList(status); + List list = chatModelService.getChatModelListByStatus(status); return success(convertList(list, model -> new AiChatModelRespVO().setId(model.getId()) .setName(model.getName()).setModel(model.getModel()))); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatModelMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatModelMapper.java deleted file mode 100644 index 25687d0..0000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatModelMapper.java +++ /dev/null @@ -1,53 +0,0 @@ -package cn.iocoder.yudao.module.ai.dal.mysql; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.Collection; -import java.util.List; - -/** - * API 聊天模型 Mapper - * - * @author fansili - */ -@Mapper -public interface AiChatModelMapper extends BaseMapperX { - - default AiChatModelDO selectFirstByStatus(Integer status) { - return selectOne(new QueryWrapperX() - .eq("status", status) - .limitN(1) - .orderByAsc("sort")); - } - - // TODO 芋艿:不需要哈 - /** - * 查询 - 根据 ids - * - * @param modalIds - * @return - */ - default List selectByIds(Collection modalIds) { - return this.selectList(new LambdaQueryWrapperX().eq(AiChatModelDO::getId, modalIds)); - } - - default PageResult selectPage(AiChatModelPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(AiChatModelDO::getName, reqVO.getName()) - .eqIfPresent(AiChatModelDO::getModel, reqVO.getModel()) - .eqIfPresent(AiChatModelDO::getPlatform, reqVO.getPlatform()) - .orderByAsc(AiChatModelDO::getSort)); - } - - default List selectList(Integer status) { - return selectList(new LambdaQueryWrapperX() - .eq(AiChatModelDO::getStatus, status) - .orderByAsc(AiChatModelDO::getSort)); - } -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatRoleMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatRoleMapper.java deleted file mode 100644 index 42a3cb1..0000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatRoleMapper.java +++ /dev/null @@ -1,50 +0,0 @@ -package cn.iocoder.yudao.module.ai.dal.mysql; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRolePageReqVO; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -/** - * AI 聊天角色 Mapper - * - * @author fansili - */ -@Mapper -public interface AiChatRoleMapper extends BaseMapperX { - - default PageResult selectPage(AiChatRolePageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(AiChatRoleDO::getName, reqVO.getName()) - .eqIfPresent(AiChatRoleDO::getCategory, reqVO.getCategory()) - .eqIfPresent(AiChatRoleDO::getPublicStatus, reqVO.getPublicStatus()) - .orderByAsc(AiChatRoleDO::getSort)); - } - - default PageResult selectPageByMy(AiChatRolePageReqVO reqVO, Long userId) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(AiChatRoleDO::getName, reqVO.getName()) - .eqIfPresent(AiChatRoleDO::getCategory, reqVO.getCategory()) - // 情况一:公开 - .eq(Boolean.TRUE.equals(reqVO.getPublicStatus()), AiChatRoleDO::getPublicStatus, reqVO.getPublicStatus()) - // 情况二:私有 - .eq(Boolean.FALSE.equals(reqVO.getPublicStatus()), AiChatRoleDO::getUserId, userId) - .eq(Boolean.FALSE.equals(reqVO.getPublicStatus()), AiChatRoleDO::getStatus, CommonStatusEnum.ENABLE.getStatus()) - .orderByAsc(AiChatRoleDO::getSort)); - } - - default List selectListGroupByCategory(Integer status) { - return selectList(new LambdaQueryWrapperX() - .select(AiChatRoleDO::getCategory) - .eq(AiChatRoleDO::getStatus, status) - .groupBy(AiChatRoleDO::getCategory)); - } - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiImageMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiImageMapper.java deleted file mode 100644 index d353fea..0000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiImageMapper.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.ai.dal.mysql; - -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; -import org.apache.ibatis.annotations.Mapper; -import org.springframework.stereotype.Repository; - -/** - * ai image - * - * @author fansili - * @time 2024/4/28 14:01 - * @since 1.0 - */ -@Mapper -public interface AiImageMapper extends BaseMapperX { - - - /** - * 更新 - 根据 messageId - * - * @param mjNonceId - * @param aiImageDO - */ - default void updateByMjNonce(Long mjNonceId, AiImageDO aiImageDO) { - this.update(aiImageDO, new LambdaQueryWrapperX().eq(AiImageDO::getMjNonceId, mjNonceId)); - } -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/image/AiImageMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/image/AiImageMapper.java new file mode 100644 index 0000000..9e75cb4 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/image/AiImageMapper.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.ai.dal.mysql.image; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + +/** + * ai image + * + * @author fansili + * @time 2024/4/28 14:01 + * @since 1.0 + */ +@Mapper +public interface AiImageMapper extends BaseMapperX { + + + /** + * 更新 - 根据 messageId + * + * @param mjNonceId + * @param aiImageDO + */ + default void updateByMjNonce(Long mjNonceId, AiImageDO aiImageDO) { + this.update(aiImageDO, new LambdaQueryWrapperX().eq(AiImageDO::getMjNonceId, mjNonceId)); + } + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatModelMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatModelMapper.java new file mode 100644 index 0000000..a3136fa --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatModelMapper.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.ai.dal.mysql.model; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * API 聊天模型 Mapper + * + * @author fansili + */ +@Mapper +public interface AiChatModelMapper extends BaseMapperX { + + default AiChatModelDO selectFirstByStatus(Integer status) { + return selectOne(new QueryWrapperX() + .eq("status", status) + .limitN(1) + .orderByAsc("sort")); + } + + default PageResult selectPage(AiChatModelPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(AiChatModelDO::getName, reqVO.getName()) + .eqIfPresent(AiChatModelDO::getModel, reqVO.getModel()) + .eqIfPresent(AiChatModelDO::getPlatform, reqVO.getPlatform()) + .orderByAsc(AiChatModelDO::getSort)); + } + + default List selectList(Integer status) { + return selectList(new LambdaQueryWrapperX() + .eq(AiChatModelDO::getStatus, status) + .orderByAsc(AiChatModelDO::getSort)); + } + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatRoleMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatRoleMapper.java new file mode 100644 index 0000000..1ddef83 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatRoleMapper.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.ai.dal.mysql.model; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRolePageReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * AI 聊天角色 Mapper + * + * @author fansili + */ +@Mapper +public interface AiChatRoleMapper extends BaseMapperX { + + default PageResult selectPage(AiChatRolePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(AiChatRoleDO::getName, reqVO.getName()) + .eqIfPresent(AiChatRoleDO::getCategory, reqVO.getCategory()) + .eqIfPresent(AiChatRoleDO::getPublicStatus, reqVO.getPublicStatus()) + .orderByAsc(AiChatRoleDO::getSort)); + } + + default PageResult selectPageByMy(AiChatRolePageReqVO reqVO, Long userId) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(AiChatRoleDO::getName, reqVO.getName()) + .eqIfPresent(AiChatRoleDO::getCategory, reqVO.getCategory()) + // 情况一:公开 + .eq(Boolean.TRUE.equals(reqVO.getPublicStatus()), AiChatRoleDO::getPublicStatus, reqVO.getPublicStatus()) + // 情况二:私有 + .eq(Boolean.FALSE.equals(reqVO.getPublicStatus()), AiChatRoleDO::getUserId, userId) + .eq(Boolean.FALSE.equals(reqVO.getPublicStatus()), AiChatRoleDO::getStatus, CommonStatusEnum.ENABLE.getStatus()) + .orderByAsc(AiChatRoleDO::getSort)); + } + + default List selectListGroupByCategory(Integer status) { + return selectList(new LambdaQueryWrapperX() + .select(AiChatRoleDO::getCategory) + .eq(AiChatRoleDO::getStatus, status) + .groupBy(AiChatRoleDO::getCategory)); + } + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalChatConfigVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalChatConfigVO.java deleted file mode 100644 index 79cce7a..0000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalChatConfigVO.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.ai.dal.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Data; -import lombok.experimental.Accessors; - -/** - * chat config - * - * @author fansili - * @time 2024/5/6 15:06 - * @since 1.0 - */ -@Data -@Accessors(chain = true) -public class AiChatModalChatConfigVO extends AiChatModalConfigVO { - - @NotNull - @Schema(description = "在生成消息时采用的Top-K采样大小") - private Double topK; - - @NotNull - @Schema(description = "Top-P核采样方法的概率阈值") - private Double topP; - - @NotNull - @Schema(description = "温度参数,用于调整生成回复的随机性和多样性程度") - private Double temperature; - - @NotNull - @Schema(description = "最大 tokens") - private Integer maxTokens; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalConfigVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalConfigVO.java deleted file mode 100644 index 4ed060e..0000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalConfigVO.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.iocoder.yudao.module.ai.dal.vo; - -import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; -import lombok.Data; -import lombok.experimental.Accessors; -import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel; -import cn.iocoder.yudao.framework.ai.core.model.yiyan.api.YiYanChatModel; - -/** - * modal config - * - * @author fansili - * @time 2024/5/6 15:06 - * @since 1.0 - */ -@Data -@Accessors(chain = true) -public class AiChatModalConfigVO { - - /** - * 模型平台 (冗余,方便类型转换) - * 参考:{@link AiPlatformEnum} - */ - private String platform; - /** - * 模型类型(冗余,方便类型转换) - * {@link YiYanChatModel} - * {@link XingHuoChatModel} - */ - private String type; -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalDallConfigVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalDallConfigVO.java deleted file mode 100644 index c828c9c..0000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalDallConfigVO.java +++ /dev/null @@ -1,41 +0,0 @@ -package cn.iocoder.yudao.module.ai.dal.vo; - -import cn.iocoder.yudao.framework.ai.core.enums.OpenAiImageStyleEnum; -import lombok.Data; -import lombok.experimental.Accessors; - -/** - * dall - * - * @author fansili - * @time 2024/5/6 15:06 - * @since 1.0 - */ -@Data -@Accessors(chain = true) -public class AiChatModalDallConfigVO extends AiChatModalConfigVO { - // 可选字段,默认为1 - // 生成图像的数量,必须在1到10之间。对于dall-e-3模型,目前仅支持n=1。 - private Integer n = 1; - - // 可选字段,默认为standard - // 设置生成图像的质量。hd质量将创建细节更丰富、图像整体一致性更高的图片。该参数仅对dall-e-3模型有效。 - private String quality = "standard"; - - // 可选字段,默认为url - // 返回生成图像的格式。必须是url或b64_json中的一种。URL链接的有效期是从生成图像后开始计算的60分钟内有效。 - private String responseFormat = "url"; - - // 可选字段,默认为1024x1024 - // 生成图像的尺寸大小。对于dall-e-2模型,尺寸可为256x256, 512x512, 或 1024x1024。对于dall-e-3模型,尺寸可为1024x1024, 1792x1024, 或 1024x1792。 - private String size = "1024x1024"; - - // 可选字段,默认为vivid - // 图像生成的风格。可为vivid(生动)或natural(自然)。vivid会使模型偏向生成超现实和戏剧性的图像,而natural则会让模型产出更自然、不那么超现实的图像。该参数仅对dall-e-3模型有效。 - private OpenAiImageStyleEnum style = OpenAiImageStyleEnum.VIVID; - - // 可选字段 - // 代表您的终端用户的唯一标识符,有助于OpenAI监控并检测滥用行为。了解更多信息请参考官方文档。 - private String endUserId = "UID123456"; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalMidjourneyConfigVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalMidjourneyConfigVO.java deleted file mode 100644 index 543c587..0000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalMidjourneyConfigVO.java +++ /dev/null @@ -1,16 +0,0 @@ -package cn.iocoder.yudao.module.ai.dal.vo; - -import lombok.Data; -import lombok.experimental.Accessors; - -/** - * Midjourney Config - * - * @author fansili - * @time 2024/5/6 15:07 - * @since 1.0 - */ -@Data -@Accessors(chain = true) -public class AiChatModalMidjourneyConfigVO extends AiChatModalConfigVO { -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/typeHandler/AiChatModelConfigTypeHandler.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/typeHandler/AiChatModelConfigTypeHandler.java deleted file mode 100644 index b1f54ea..0000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/typeHandler/AiChatModelConfigTypeHandler.java +++ /dev/null @@ -1,75 +0,0 @@ -//package cn.iocoder.yudao.module.ai.mapper.typeHandler; -// -//import cn.hutool.core.util.StrUtil; -//import cn.iocoder.yudao.framework.ai.AiPlatformEnum; -//import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -//import cn.iocoder.yudao.module.ai.dal.dataobject.AiChatModalDO; -//import org.apache.ibatis.type.BaseTypeHandler; -//import org.apache.ibatis.type.JdbcType; -//import org.apache.ibatis.type.MappedTypes; -// -//import java.sql.CallableStatement; -//import java.sql.PreparedStatement; -//import java.sql.ResultSet; -//import java.sql.SQLException; -// -///** -// * chat modal config -// * -// * @author fansili -// * @time 2024/5/6 11:18 -// * @since 1.0 -// */ -//@MappedTypes(value = AiChatModalDO.Config.class) -//public class AiChatModelConfigTypeHandler extends BaseTypeHandler { -// -// @Override -// public void setNonNullParameter(PreparedStatement ps, int i, AiChatModalDO.Config parameter, JdbcType jdbcType) throws SQLException { -// // 将 MyCustomType 转换为数据库类型并设置到 PreparedStatement 中 -// if (parameter == null) { -// ps.setString(i, ""); -// } else { -// ps.setString(i, JsonUtils.toJsonString(parameter)); -// } -// } -// -// @Override -// public AiChatModalDO.Config getNullableResult(ResultSet rs, String columnName) throws SQLException { -// // 从 ResultSet 中获取数据库类型并转换为 MyCustomType -// String str = rs.getString(columnName); -// if (StrUtil.isBlank(str)) { -// return null; -// } -// AiChatModalDO.Config config = JsonUtils.parseObject(str, AiChatModalDO.Config.class); -// // 获取平台 -// AiPlatformEnum platformEnum = AiPlatformEnum.valueOfPlatform(config.getModelPlatform()); -// if (AiPlatformEnum.CHAT_PLATFORM_LIST.contains(platformEnum)) { -// return JsonUtils.parseObject(str, AiChatModalDO.ChatConfig.class); -// } else if (AiPlatformEnum.OPEN_AI_DALL == platformEnum) { -// return JsonUtils.parseObject(str, AiChatModalDO.OpenAiImageConfig.class); -// } else if (AiPlatformEnum.MIDJOURNEY == platformEnum) { -// return JsonUtils.parseObject(str, AiChatModalDO.MidjourneyConfig.class); -// } -// throw new IllegalArgumentException("ai模型中config不能转换! json: " + str); -// } -// -// @Override -// public AiChatModalDO.Config getNullableResult(ResultSet rs, int columnIndex) throws SQLException { -// // 从 ResultSet 中获取数据库类型并转换为 MyCustomType -// String str = rs.getString(columnIndex); -// if (StrUtil.isBlank(str)) { -// return null; -// } -// return JsonUtils.parseObject(str, AiChatModalDO.Config.class); -// } -// -// @Override -// public AiChatModalDO.Config getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { -// // 从 CallableStatement 中获取数据库类型并转换为 MyCustomType -// String str = cs.getString(columnIndex); -// if (StrUtil.isBlank(str)) { -// return null; -// } -// return JsonUtils.parseObject(str, AiChatModalDO.Config.class); -// } -//} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiImageService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiImageService.java deleted file mode 100644 index b582010..0000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiImageService.java +++ /dev/null @@ -1,54 +0,0 @@ -package cn.iocoder.yudao.module.ai.service; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.*; - -import java.util.List; - -/** - * ai 作图 - * - * @author fansili - * @time 2024/4/25 15:50 - * @since 1.0 - */ -public interface AiImageService { - - /** - * ai绘画 - 列表 - * - * @param req - * @return - */ - PageResult list(AiImageListReqVO req); - - /** - * ai绘画 - dall2/dall3 绘画 - * - * @param req - */ - AiImageDallDrawingRespVO dallDrawing(AiImageDallDrawingReqVO req); - - /** - * midjourney 图片生成 - * - * @param req - * @return - */ - void midjourney(AiImageMidjourneyReqVO req); - - /** - * midjourney 操作(u1、u2、放大、换一批...) - * - * @param req - */ - void midjourneyOperate(AiImageMidjourneyOperateReqVO req); - - /** - * 删除 - image 记录 - * - * @param id - */ - void delete(Long id); - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageService.java index fcf97a7..d7f3e80 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageService.java @@ -45,7 +45,7 @@ public interface AiChatMessageService { * @param id 消息编号 * @param userId 用户编号 */ - void deleteMessage(Long id, Long userId); + void deleteChatMessage(Long id, Long userId); /** * 删除指定会话的消息 diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java index a686124..8da9110 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java @@ -40,6 +40,8 @@ import java.time.LocalDateTime; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.AI_CHAT_MESSAGE_NOT_EXIST; import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS; @@ -138,7 +140,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { Flux streamResponse = chatClient.stream(prompt); // 3.3 流式返回 - // 注意:Schedulers.immediate() 目的是,避免默认 Schedulers.parallel() 并发消费 chunk 导致 SSE 响应前端会乱序问题 + // TODO 注意:Schedulers.immediate() 目的是,避免默认 Schedulers.parallel() 并发消费 chunk 导致 SSE 响应前端会乱序问题 StringBuffer contentBuffer = new StringBuffer(); return streamResponse.map(chunk -> { String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getContent() : null; @@ -149,14 +151,14 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { o -> o.setUserAvatar(user.getAvatar())); AiChatMessageSendRespVO.Message receive = BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class, o -> o.setRoleAvatar(role != null ? role.getAvatar() : null)).setContent(newContent); - return CommonResult.success(new AiChatMessageSendRespVO().setSend(send).setReceive(receive)); + return success(new AiChatMessageSendRespVO().setSend(send).setReceive(receive)); }).doOnComplete(() -> { chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(contentBuffer.toString())); }).doOnError(throwable -> { log.error("[sendChatMessageStream][userId({}) sendReqVO({}) 发生异常]", userId, sendReqVO, throwable); chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(throwable.getMessage())); - }).onErrorResume( error -> { - return Flux.just(CommonResult.error(ErrorCodeConstants.AI_CHAT_STREAM_ERROR)); + }).onErrorResume(error -> { + return Flux.just(error(ErrorCodeConstants.AI_CHAT_STREAM_ERROR)); }); } @@ -235,7 +237,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { } @Override - public void deleteMessage(Long id, Long userId) { + public void deleteChatMessage(Long id, Long userId) { // 1. 校验消息存在 AiChatMessageDO message = chatMessageMapper.selectById(id); if (message == null || ObjUtil.notEqual(message.getUserId(), userId)) { diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageService.java new file mode 100644 index 0000000..836f37d --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageService.java @@ -0,0 +1,54 @@ +package cn.iocoder.yudao.module.ai.service.image; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.*; + +import java.util.List; + +/** + * ai 作图 + * + * @author fansili + * @time 2024/4/25 15:50 + * @since 1.0 + */ +public interface AiImageService { + + /** + * ai绘画 - 列表 + * + * @param req + * @return + */ + PageResult list(AiImageListReqVO req); + + /** + * ai绘画 - dall2/dall3 绘画 + * + * @param req + */ + AiImageDallDrawingRespVO dallDrawing(AiImageDallDrawingReqVO req); + + /** + * midjourney 图片生成 + * + * @param req + * @return + */ + void midjourney(AiImageMidjourneyReqVO req); + + /** + * midjourney 操作(u1、u2、放大、换一批...) + * + * @param req + */ + void midjourneyOperate(AiImageMidjourneyOperateReqVO req); + + /** + * 删除 - image 记录 + * + * @param id + */ + void delete(Long id); + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java new file mode 100644 index 0000000..f4521af --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java @@ -0,0 +1,230 @@ +package cn.iocoder.yudao.module.ai.service.image; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.ai.core.exception.AiException; +import org.springframework.ai.image.ImageGeneration; +import org.springframework.ai.image.ImagePrompt; +import org.springframework.ai.image.ImageResponse; +import cn.iocoder.yudao.framework.ai.core.enums.OpenAiImageModelEnum; +import cn.iocoder.yudao.framework.ai.core.enums.OpenAiImageStyleEnum; +import org.springframework.ai.models.midjourney.api.MidjourneyInteractionsApi; +import org.springframework.ai.models.midjourney.api.req.ReRollReq; +import org.springframework.ai.models.midjourney.webSocket.MidjourneyWebSocketStarter; +import org.springframework.ai.models.midjourney.webSocket.WssNotify; +import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; +import cn.iocoder.yudao.module.ai.ErrorCodeConstants; +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.*; +import cn.iocoder.yudao.module.ai.convert.AiImageConvert; +import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; +import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper; +import cn.iocoder.yudao.module.ai.enums.AiImageDrawingStatusEnum; +import jakarta.annotation.PostConstruct; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.openai.OpenAiImageClient; +import org.springframework.ai.openai.OpenAiImageOptions; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collections; +import java.util.List; + +/** + * ai 作图 + * + * @author fansili + * @time 2024/4/25 15:51 + * @since 1.0 + */ +@AllArgsConstructor +@Service +@Slf4j +public class AiImageServiceImpl implements AiImageService { + + private final AiImageMapper aiImageMapper; + private final OpenAiImageClient openAiImageClient; + private final MidjourneyWebSocketStarter midjourneyWebSocketStarter; + private final MidjourneyInteractionsApi midjourneyInteractionsApi; + + @PostConstruct + public void startMidjourney() { + log.info("midjourney web socket starter..."); + midjourneyWebSocketStarter.start(new WssNotify() { + @Override + public void notify(int code, String message) { + log.info("code: {}, message: {}", code, message); + if (message.contains("Authentication failed")) { + // TODO 芋艿,这里看怎么处理,token无效的时候会认证失败! + // 认证失败 + log.error("midjourney socket 认证失败,检查token是否失效!"); + } + } + }); + } + + @Override + public PageResult list(AiImageListReqVO req) { + // 获取登录用户 + Long loginUserId = SecurityFrameworkUtils.getLoginUserId(); + // 查询当前用户下所有的绘画记录 + PageResult pageResult = aiImageMapper.selectPage(req, + new LambdaQueryWrapperX() + .eq(AiImageDO::getUserId, loginUserId) + .orderByDesc(AiImageDO::getId) + ); + // 转换 PageResult 返回 + PageResult result = new PageResult<>(); + result.setTotal(pageResult.getTotal()); + result.setList(AiImageConvert.INSTANCE.convertAiImageListRespVO(pageResult.getList())); + return result; + } + + @Override + public AiImageDallDrawingRespVO dallDrawing(AiImageDallDrawingReqVO req) { + // 获取 model + OpenAiImageModelEnum openAiImageModelEnum = OpenAiImageModelEnum.valueOfModel(req.getModal()); + OpenAiImageStyleEnum openAiImageStyleEnum = OpenAiImageStyleEnum.valueOfStyle(req.getStyle()); + // 转换 AiImageDallDrawingRespVO + AiImageDallDrawingRespVO respVO = AiImageConvert.INSTANCE.convertAiImageDallDrawingRespVO(req); + try { + // 转换openai 参数 + OpenAiImageOptions openAiImageOptions = new OpenAiImageOptions(); + openAiImageOptions.setModel(openAiImageModelEnum.getModel()); + openAiImageOptions.setStyle(openAiImageStyleEnum.getStyle()); + openAiImageOptions.setSize(req.getSize()); + ImageResponse imageResponse = openAiImageClient.call(new ImagePrompt(req.getPrompt(), openAiImageOptions)); + // 发送 + ImageGeneration imageGeneration = imageResponse.getResult(); + // 保存数据库 + doSave(req.getPrompt(), req.getSize(), req.getModal(), + imageGeneration.getOutput().getUrl(), AiImageDrawingStatusEnum.COMPLETE, null, + null, null, null); + // 返回 flex + respVO.setUrl(imageGeneration.getOutput().getUrl()); + respVO.setBase64(imageGeneration.getOutput().getB64Json()); + return respVO; + } catch (AiException aiException) { + // 保存数据库 + doSave(req.getPrompt(), req.getSize(), req.getModal(), + null, AiImageDrawingStatusEnum.FAIL, aiException.getMessage(), + null, null, null); + // 发送错误信息 + respVO.setErrorMessage(aiException.getMessage()); + return respVO; + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void midjourney(AiImageMidjourneyReqVO req) { + // 保存数据库 + String messageId = String.valueOf(IdUtil.getSnowflakeNextId()); + AiImageDO aiImageDO = doSave(req.getPrompt(), null, "midjoureny", + null, AiImageDrawingStatusEnum.SUBMIT, null, + messageId, null, null); + // 提交 midjourney 任务 + Boolean imagine = midjourneyInteractionsApi.imagine(messageId, req.getPrompt()); + if (!imagine) { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MIDJOURNEY_IMAGINE_FAIL); + } + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void midjourneyOperate(AiImageMidjourneyOperateReqVO req) { + // 校验是否存在 + AiImageDO aiImageDO = validateExists(req.getId()); + // 获取 midjourneyOperations + List midjourneyOperations = getMidjourneyOperations(aiImageDO); + // 校验 OperateId 是否存在 + AiImageMidjourneyOperationsVO midjourneyOperationsVO = validateMidjourneyOperationsExists(midjourneyOperations, req.getOperateId()); + // 校验 messageId + validateMessageId(aiImageDO.getMjNonceId(), req.getMessageId()); + // 获取 mjOperationName + String mjOperationName = midjourneyOperationsVO.getLabel(); + // 保存一个 image 任务记录 + doSave(aiImageDO.getPrompt(), aiImageDO.getSize(), aiImageDO.getModal(), + null, AiImageDrawingStatusEnum.SUBMIT, null, + req.getMessageId(), req.getOperateId(), mjOperationName); + // 提交操作 + midjourneyInteractionsApi.reRoll( + new ReRollReq() + .setCustomId(req.getOperateId()) + .setMessageId(req.getMessageId()) + ); + } + + @Override + public void delete(Long id) { + // 校验记录是否存在 + AiImageDO aiImageDO = validateExists(id); + // 删除记录 + aiImageMapper.deleteById(id); + } + + private void validateMessageId(String mjMessageId, String messageId) { + if (!mjMessageId.equals(messageId)) { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MIDJOURNEY_MESSAGE_ID_INCORRECT); + } + } + + private AiImageMidjourneyOperationsVO validateMidjourneyOperationsExists(List midjourneyOperations, String operateId) { + for (AiImageMidjourneyOperationsVO midjourneyOperation : midjourneyOperations) { + if (midjourneyOperation.getCustom_id().equals(operateId)) { + return midjourneyOperation; + } + } + throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MIDJOURNEY_OPERATION_NOT_EXISTS); + } + + + private List getMidjourneyOperations(AiImageDO aiImageDO) { + if (StrUtil.isBlank(aiImageDO.getMjOperations())) { + return Collections.emptyList(); + } + return JsonUtils.parseArray(aiImageDO.getMjOperations(), AiImageMidjourneyOperationsVO.class); + } + + private AiImageDO validateExists(Long id) { + AiImageDO aiImageDO = aiImageMapper.selectById(id); + if (aiImageDO == null) { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MIDJOURNEY_IMAGINE_FAIL); + } + return aiImageDO; + } + + private AiImageDO doSave(String prompt, + String size, + String model, + String drawingImageUrl, + AiImageDrawingStatusEnum drawingStatusEnum, + String drawingErrorMessage, + String mjMessageId, + String mjOperationId, + String mjOperationName) { + // 保存数据库 + Long loginUserId = SecurityFrameworkUtils.getLoginUserId(); + AiImageDO aiImageDO = new AiImageDO(); + aiImageDO.setId(null); + aiImageDO.setPrompt(prompt); + aiImageDO.setSize(size); + aiImageDO.setModal(model); + aiImageDO.setUserId(loginUserId); + // TODO @芋艿 如何上传到自己服务器 + aiImageDO.setImageUrl(null); + aiImageDO.setDrawingStatus(drawingStatusEnum.getStatus()); + aiImageDO.setDrawingImageUrl(drawingImageUrl); + aiImageDO.setDrawingErrorMessage(drawingErrorMessage); + // + aiImageDO.setMjNonceId(mjMessageId); + aiImageDO.setMjOperationId(mjOperationId); + aiImageDO.setMjOperationName(mjOperationName); + aiImageMapper.insert(aiImageDO); + return aiImageDO; + } +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/YuDaoMidjourneyMessageHandler.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/YuDaoMidjourneyMessageHandler.java new file mode 100644 index 0000000..5327d65 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/YuDaoMidjourneyMessageHandler.java @@ -0,0 +1,126 @@ +package cn.iocoder.yudao.module.ai.service.image.midjourneyHandler; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSON; +import org.springframework.ai.models.midjourney.MidjourneyMessage; +import org.springframework.ai.models.midjourney.constants.MidjourneyGennerateStatusEnum; +import org.springframework.ai.models.midjourney.webSocket.MidjourneyMessageHandler; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyOperationsVO; +import cn.iocoder.yudao.module.ai.convert.AiImageConvert; +import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; +import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper; +import cn.iocoder.yudao.module.ai.enums.AiImageDrawingStatusEnum; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * yudao message handler + * + * @author fansili + * @time 2024/4/29 14:34 + * @since 1.0 + */ +@Component +@Slf4j +@AllArgsConstructor +public class YuDaoMidjourneyMessageHandler implements MidjourneyMessageHandler { + + private final AiImageMapper aiImageMapper; + + @Override + public void messageHandler(MidjourneyMessage midjourneyMessage) { + log.info("yudao-midjourney-midjourney-message-handler {}", JSON.toJSONString(midjourneyMessage)); + if (midjourneyMessage.getContent() != null) { + log.info("进度id {} 状态 {} 进度 {}", + midjourneyMessage.getNonce(), + midjourneyMessage.getGenerateStatus(), + midjourneyMessage.getContent().getProgress()); + } + // + updateImage(midjourneyMessage); + } + + private void updateImage(MidjourneyMessage midjourneyMessage) { + // Nonce 不存在不更新 + if (StrUtil.isBlank(midjourneyMessage.getNonce())) { + return; + } + // 根据 Embeds 来判断是否异常 + if (CollUtil.isEmpty(midjourneyMessage.getEmbeds())) { + successHandler(midjourneyMessage); + } else { + errorHandler(midjourneyMessage); + } + } + + private void errorHandler(MidjourneyMessage midjourneyMessage) { + // image 编号 + Long nonceId = Long.valueOf(midjourneyMessage.getNonce()); + // 获取 error message + String errorMessage = getErrorMessage(midjourneyMessage); + aiImageMapper.updateByMjNonce(nonceId, + new AiImageDO() + .setDrawingErrorMessage(errorMessage) + .setDrawingStatus(AiImageDrawingStatusEnum.FAIL.getStatus()) + ); + } + + private String getErrorMessage(MidjourneyMessage midjourneyMessage) { + StringBuilder errorMessage = new StringBuilder(); + for (MidjourneyMessage.Embed embed : midjourneyMessage.getEmbeds()) { + errorMessage.append(embed.getDescription()); + } + return errorMessage.toString(); + } + + private void successHandler(MidjourneyMessage midjourneyMessage) { + // 获取id + Long nonceId = Long.valueOf(midjourneyMessage.getNonce()); + // TODO @芋艿 这个地方有问题,不能根据 nonce来更新,不返回这个信息(别人获取了 image-xxx-xx 后面一段hash,由于没有mj账号测试,暂不清楚。) + // 获取生成 url + String imageUrl = null; + if (CollUtil.isNotEmpty(midjourneyMessage.getAttachments())) { + imageUrl = midjourneyMessage.getAttachments().get(0).getUrl(); + } + // 转换状态 + AiImageDrawingStatusEnum drawingStatusEnum = null; + String generateStatus = midjourneyMessage.getGenerateStatus(); + if (MidjourneyGennerateStatusEnum.COMPLETED.getStatus().equals(generateStatus)) { + drawingStatusEnum = AiImageDrawingStatusEnum.COMPLETE; + } else if (MidjourneyGennerateStatusEnum.IN_PROGRESS.getStatus().equals(generateStatus)) { + drawingStatusEnum = AiImageDrawingStatusEnum.IN_PROGRESS; + } else if (MidjourneyGennerateStatusEnum.WAITING.getStatus().equals(generateStatus)) { + drawingStatusEnum = AiImageDrawingStatusEnum.WAITING; + } + // 获取 midjourneyOperations + List midjourneyOperations = getMidjourneyOperationsList(midjourneyMessage); + // 更新数据库 + aiImageMapper.updateByMjNonce(nonceId, + new AiImageDO() + .setDrawingImageUrl(imageUrl) + .setDrawingStatus(drawingStatusEnum == null ? null : drawingStatusEnum.getStatus()) + .setMjNonceId(midjourneyMessage.getId()) + .setMjOperations(JsonUtils.toJsonString(midjourneyOperations)) + ); + } + + private List getMidjourneyOperationsList(MidjourneyMessage midjourneyMessage) { + // 为空直接返回 + if (CollUtil.isEmpty(midjourneyMessage.getComponents())) { + return Collections.emptyList(); + } + // 将 component 转成 AiImageMidjourneyOperationsVO + return midjourneyMessage.getComponents().stream() + .map(componentType -> componentType.getComponents().stream() + .map(AiImageConvert.INSTANCE::convertAiImageMidjourneyOperationsVO) + .collect(Collectors.toList())) + .toList().stream().flatMap(List::stream).toList(); + } +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/typeHandler/AiChatModelConfigTypeHandler.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/typeHandler/AiChatModelConfigTypeHandler.java new file mode 100644 index 0000000..b1f54ea --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/typeHandler/AiChatModelConfigTypeHandler.java @@ -0,0 +1,75 @@ +//package cn.iocoder.yudao.module.ai.mapper.typeHandler; +// +//import cn.hutool.core.util.StrUtil; +//import cn.iocoder.yudao.framework.ai.AiPlatformEnum; +//import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +//import cn.iocoder.yudao.module.ai.dal.dataobject.AiChatModalDO; +//import org.apache.ibatis.type.BaseTypeHandler; +//import org.apache.ibatis.type.JdbcType; +//import org.apache.ibatis.type.MappedTypes; +// +//import java.sql.CallableStatement; +//import java.sql.PreparedStatement; +//import java.sql.ResultSet; +//import java.sql.SQLException; +// +///** +// * chat modal config +// * +// * @author fansili +// * @time 2024/5/6 11:18 +// * @since 1.0 +// */ +//@MappedTypes(value = AiChatModalDO.Config.class) +//public class AiChatModelConfigTypeHandler extends BaseTypeHandler { +// +// @Override +// public void setNonNullParameter(PreparedStatement ps, int i, AiChatModalDO.Config parameter, JdbcType jdbcType) throws SQLException { +// // 将 MyCustomType 转换为数据库类型并设置到 PreparedStatement 中 +// if (parameter == null) { +// ps.setString(i, ""); +// } else { +// ps.setString(i, JsonUtils.toJsonString(parameter)); +// } +// } +// +// @Override +// public AiChatModalDO.Config getNullableResult(ResultSet rs, String columnName) throws SQLException { +// // 从 ResultSet 中获取数据库类型并转换为 MyCustomType +// String str = rs.getString(columnName); +// if (StrUtil.isBlank(str)) { +// return null; +// } +// AiChatModalDO.Config config = JsonUtils.parseObject(str, AiChatModalDO.Config.class); +// // 获取平台 +// AiPlatformEnum platformEnum = AiPlatformEnum.valueOfPlatform(config.getModelPlatform()); +// if (AiPlatformEnum.CHAT_PLATFORM_LIST.contains(platformEnum)) { +// return JsonUtils.parseObject(str, AiChatModalDO.ChatConfig.class); +// } else if (AiPlatformEnum.OPEN_AI_DALL == platformEnum) { +// return JsonUtils.parseObject(str, AiChatModalDO.OpenAiImageConfig.class); +// } else if (AiPlatformEnum.MIDJOURNEY == platformEnum) { +// return JsonUtils.parseObject(str, AiChatModalDO.MidjourneyConfig.class); +// } +// throw new IllegalArgumentException("ai模型中config不能转换! json: " + str); +// } +// +// @Override +// public AiChatModalDO.Config getNullableResult(ResultSet rs, int columnIndex) throws SQLException { +// // 从 ResultSet 中获取数据库类型并转换为 MyCustomType +// String str = rs.getString(columnIndex); +// if (StrUtil.isBlank(str)) { +// return null; +// } +// return JsonUtils.parseObject(str, AiChatModalDO.Config.class); +// } +// +// @Override +// public AiChatModalDO.Config getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { +// // 从 CallableStatement 中获取数据库类型并转换为 MyCustomType +// String str = cs.getString(columnIndex); +// if (StrUtil.isBlank(str)) { +// return null; +// } +// return JsonUtils.parseObject(str, AiChatModalDO.Config.class); +// } +//} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalChatConfigVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalChatConfigVO.java new file mode 100644 index 0000000..c3d2b77 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalChatConfigVO.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.ai.service.image.midjourneyHandler.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * chat config + * + * @author fansili + * @time 2024/5/6 15:06 + * @since 1.0 + */ +@Data +@Accessors(chain = true) +public class AiChatModalChatConfigVO extends AiChatModalConfigVO { + + @NotNull + @Schema(description = "在生成消息时采用的Top-K采样大小") + private Double topK; + + @NotNull + @Schema(description = "Top-P核采样方法的概率阈值") + private Double topP; + + @NotNull + @Schema(description = "温度参数,用于调整生成回复的随机性和多样性程度") + private Double temperature; + + @NotNull + @Schema(description = "最大 tokens") + private Integer maxTokens; + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalConfigVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalConfigVO.java new file mode 100644 index 0000000..426641a --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalConfigVO.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.ai.service.image.midjourneyHandler.vo; + +import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; +import lombok.Data; +import lombok.experimental.Accessors; +import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel; +import cn.iocoder.yudao.framework.ai.core.model.yiyan.api.YiYanChatModel; + +/** + * modal config + * + * @author fansili + * @time 2024/5/6 15:06 + * @since 1.0 + */ +@Data +@Accessors(chain = true) +public class AiChatModalConfigVO { + + /** + * 模型平台 (冗余,方便类型转换) + * 参考:{@link AiPlatformEnum} + */ + private String platform; + /** + * 模型类型(冗余,方便类型转换) + * {@link YiYanChatModel} + * {@link XingHuoChatModel} + */ + private String type; +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalDallConfigVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalDallConfigVO.java new file mode 100644 index 0000000..f07b446 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalDallConfigVO.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.ai.service.image.midjourneyHandler.vo; + +import cn.iocoder.yudao.framework.ai.core.enums.OpenAiImageStyleEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * dall + * + * @author fansili + * @time 2024/5/6 15:06 + * @since 1.0 + */ +@Data +@Accessors(chain = true) +public class AiChatModalDallConfigVO extends AiChatModalConfigVO { + // 可选字段,默认为1 + // 生成图像的数量,必须在1到10之间。对于dall-e-3模型,目前仅支持n=1。 + private Integer n = 1; + + // 可选字段,默认为standard + // 设置生成图像的质量。hd质量将创建细节更丰富、图像整体一致性更高的图片。该参数仅对dall-e-3模型有效。 + private String quality = "standard"; + + // 可选字段,默认为url + // 返回生成图像的格式。必须是url或b64_json中的一种。URL链接的有效期是从生成图像后开始计算的60分钟内有效。 + private String responseFormat = "url"; + + // 可选字段,默认为1024x1024 + // 生成图像的尺寸大小。对于dall-e-2模型,尺寸可为256x256, 512x512, 或 1024x1024。对于dall-e-3模型,尺寸可为1024x1024, 1792x1024, 或 1024x1792。 + private String size = "1024x1024"; + + // 可选字段,默认为vivid + // 图像生成的风格。可为vivid(生动)或natural(自然)。vivid会使模型偏向生成超现实和戏剧性的图像,而natural则会让模型产出更自然、不那么超现实的图像。该参数仅对dall-e-3模型有效。 + private OpenAiImageStyleEnum style = OpenAiImageStyleEnum.VIVID; + + // 可选字段 + // 代表您的终端用户的唯一标识符,有助于OpenAI监控并检测滥用行为。了解更多信息请参考官方文档。 + private String endUserId = "UID123456"; + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalMidjourneyConfigVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalMidjourneyConfigVO.java new file mode 100644 index 0000000..6c91ca6 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalMidjourneyConfigVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.ai.service.image.midjourneyHandler.vo; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * Midjourney Config + * + * @author fansili + * @time 2024/5/6 15:07 + * @since 1.0 + */ +@Data +@Accessors(chain = true) +public class AiChatModalMidjourneyConfigVO extends AiChatModalConfigVO { +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiImageServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiImageServiceImpl.java deleted file mode 100644 index 20768c2..0000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiImageServiceImpl.java +++ /dev/null @@ -1,231 +0,0 @@ -package cn.iocoder.yudao.module.ai.service.impl; - -import cn.hutool.core.util.IdUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.ai.core.exception.AiException; -import org.springframework.ai.image.ImageGeneration; -import org.springframework.ai.image.ImagePrompt; -import org.springframework.ai.image.ImageResponse; -import cn.iocoder.yudao.framework.ai.core.enums.OpenAiImageModelEnum; -import cn.iocoder.yudao.framework.ai.core.enums.OpenAiImageStyleEnum; -import org.springframework.ai.models.midjourney.api.MidjourneyInteractionsApi; -import org.springframework.ai.models.midjourney.api.req.ReRollReq; -import org.springframework.ai.models.midjourney.webSocket.MidjourneyWebSocketStarter; -import org.springframework.ai.models.midjourney.webSocket.WssNotify; -import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; -import cn.iocoder.yudao.module.ai.ErrorCodeConstants; -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.*; -import cn.iocoder.yudao.module.ai.convert.AiImageConvert; -import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; -import cn.iocoder.yudao.module.ai.dal.mysql.AiImageMapper; -import cn.iocoder.yudao.module.ai.enums.AiImageDrawingStatusEnum; -import cn.iocoder.yudao.module.ai.service.AiImageService; -import jakarta.annotation.PostConstruct; -import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.ai.openai.OpenAiImageClient; -import org.springframework.ai.openai.OpenAiImageOptions; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Collections; -import java.util.List; - -/** - * ai 作图 - * - * @author fansili - * @time 2024/4/25 15:51 - * @since 1.0 - */ -@AllArgsConstructor -@Service -@Slf4j -public class AiImageServiceImpl implements AiImageService { - - private final AiImageMapper aiImageMapper; - private final OpenAiImageClient openAiImageClient; - private final MidjourneyWebSocketStarter midjourneyWebSocketStarter; - private final MidjourneyInteractionsApi midjourneyInteractionsApi; - - @PostConstruct - public void startMidjourney() { - log.info("midjourney web socket starter..."); - midjourneyWebSocketStarter.start(new WssNotify() { - @Override - public void notify(int code, String message) { - log.info("code: {}, message: {}", code, message); - if (message.contains("Authentication failed")) { - // TODO 芋艿,这里看怎么处理,token无效的时候会认证失败! - // 认证失败 - log.error("midjourney socket 认证失败,检查token是否失效!"); - } - } - }); - } - - @Override - public PageResult list(AiImageListReqVO req) { - // 获取登录用户 - Long loginUserId = SecurityFrameworkUtils.getLoginUserId(); - // 查询当前用户下所有的绘画记录 - PageResult pageResult = aiImageMapper.selectPage(req, - new LambdaQueryWrapperX() - .eq(AiImageDO::getUserId, loginUserId) - .orderByDesc(AiImageDO::getId) - ); - // 转换 PageResult 返回 - PageResult result = new PageResult<>(); - result.setTotal(pageResult.getTotal()); - result.setList(AiImageConvert.INSTANCE.convertAiImageListRespVO(pageResult.getList())); - return result; - } - - @Override - public AiImageDallDrawingRespVO dallDrawing(AiImageDallDrawingReqVO req) { - // 获取 model - OpenAiImageModelEnum openAiImageModelEnum = OpenAiImageModelEnum.valueOfModel(req.getModal()); - OpenAiImageStyleEnum openAiImageStyleEnum = OpenAiImageStyleEnum.valueOfStyle(req.getStyle()); - // 转换 AiImageDallDrawingRespVO - AiImageDallDrawingRespVO respVO = AiImageConvert.INSTANCE.convertAiImageDallDrawingRespVO(req); - try { - // 转换openai 参数 - OpenAiImageOptions openAiImageOptions = new OpenAiImageOptions(); - openAiImageOptions.setModel(openAiImageModelEnum.getModel()); - openAiImageOptions.setStyle(openAiImageStyleEnum.getStyle()); - openAiImageOptions.setSize(req.getSize()); - ImageResponse imageResponse = openAiImageClient.call(new ImagePrompt(req.getPrompt(), openAiImageOptions)); - // 发送 - ImageGeneration imageGeneration = imageResponse.getResult(); - // 保存数据库 - doSave(req.getPrompt(), req.getSize(), req.getModal(), - imageGeneration.getOutput().getUrl(), AiImageDrawingStatusEnum.COMPLETE, null, - null, null, null); - // 返回 flex - respVO.setUrl(imageGeneration.getOutput().getUrl()); - respVO.setBase64(imageGeneration.getOutput().getB64Json()); - return respVO; - } catch (AiException aiException) { - // 保存数据库 - doSave(req.getPrompt(), req.getSize(), req.getModal(), - null, AiImageDrawingStatusEnum.FAIL, aiException.getMessage(), - null, null, null); - // 发送错误信息 - respVO.setErrorMessage(aiException.getMessage()); - return respVO; - } - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void midjourney(AiImageMidjourneyReqVO req) { - // 保存数据库 - String messageId = String.valueOf(IdUtil.getSnowflakeNextId()); - AiImageDO aiImageDO = doSave(req.getPrompt(), null, "midjoureny", - null, AiImageDrawingStatusEnum.SUBMIT, null, - messageId, null, null); - // 提交 midjourney 任务 - Boolean imagine = midjourneyInteractionsApi.imagine(messageId, req.getPrompt()); - if (!imagine) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MIDJOURNEY_IMAGINE_FAIL); - } - } - - @Transactional(rollbackFor = Exception.class) - @Override - public void midjourneyOperate(AiImageMidjourneyOperateReqVO req) { - // 校验是否存在 - AiImageDO aiImageDO = validateExists(req.getId()); - // 获取 midjourneyOperations - List midjourneyOperations = getMidjourneyOperations(aiImageDO); - // 校验 OperateId 是否存在 - AiImageMidjourneyOperationsVO midjourneyOperationsVO = validateMidjourneyOperationsExists(midjourneyOperations, req.getOperateId()); - // 校验 messageId - validateMessageId(aiImageDO.getMjNonceId(), req.getMessageId()); - // 获取 mjOperationName - String mjOperationName = midjourneyOperationsVO.getLabel(); - // 保存一个 image 任务记录 - doSave(aiImageDO.getPrompt(), aiImageDO.getSize(), aiImageDO.getModal(), - null, AiImageDrawingStatusEnum.SUBMIT, null, - req.getMessageId(), req.getOperateId(), mjOperationName); - // 提交操作 - midjourneyInteractionsApi.reRoll( - new ReRollReq() - .setCustomId(req.getOperateId()) - .setMessageId(req.getMessageId()) - ); - } - - @Override - public void delete(Long id) { - // 校验记录是否存在 - AiImageDO aiImageDO = validateExists(id); - // 删除记录 - aiImageMapper.deleteById(id); - } - - private void validateMessageId(String mjMessageId, String messageId) { - if (!mjMessageId.equals(messageId)) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MIDJOURNEY_MESSAGE_ID_INCORRECT); - } - } - - private AiImageMidjourneyOperationsVO validateMidjourneyOperationsExists(List midjourneyOperations, String operateId) { - for (AiImageMidjourneyOperationsVO midjourneyOperation : midjourneyOperations) { - if (midjourneyOperation.getCustom_id().equals(operateId)) { - return midjourneyOperation; - } - } - throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MIDJOURNEY_OPERATION_NOT_EXISTS); - } - - - private List getMidjourneyOperations(AiImageDO aiImageDO) { - if (StrUtil.isBlank(aiImageDO.getMjOperations())) { - return Collections.emptyList(); - } - return JsonUtils.parseArray(aiImageDO.getMjOperations(), AiImageMidjourneyOperationsVO.class); - } - - private AiImageDO validateExists(Long id) { - AiImageDO aiImageDO = aiImageMapper.selectById(id); - if (aiImageDO == null) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MIDJOURNEY_IMAGINE_FAIL); - } - return aiImageDO; - } - - private AiImageDO doSave(String prompt, - String size, - String model, - String drawingImageUrl, - AiImageDrawingStatusEnum drawingStatusEnum, - String drawingErrorMessage, - String mjMessageId, - String mjOperationId, - String mjOperationName) { - // 保存数据库 - Long loginUserId = SecurityFrameworkUtils.getLoginUserId(); - AiImageDO aiImageDO = new AiImageDO(); - aiImageDO.setId(null); - aiImageDO.setPrompt(prompt); - aiImageDO.setSize(size); - aiImageDO.setModal(model); - aiImageDO.setUserId(loginUserId); - // TODO @芋艿 如何上传到自己服务器 - aiImageDO.setImageUrl(null); - aiImageDO.setDrawingStatus(drawingStatusEnum.getStatus()); - aiImageDO.setDrawingImageUrl(drawingImageUrl); - aiImageDO.setDrawingErrorMessage(drawingErrorMessage); - // - aiImageDO.setMjNonceId(mjMessageId); - aiImageDO.setMjOperationId(mjOperationId); - aiImageDO.setMjOperationName(mjOperationName); - aiImageMapper.insert(aiImageDO); - return aiImageDO; - } -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/midjourneyHandler/YuDaoMidjourneyMessageHandler.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/midjourneyHandler/YuDaoMidjourneyMessageHandler.java deleted file mode 100644 index c3c9d44..0000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/midjourneyHandler/YuDaoMidjourneyMessageHandler.java +++ /dev/null @@ -1,126 +0,0 @@ -package cn.iocoder.yudao.module.ai.service.midjourneyHandler; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; -import com.alibaba.fastjson.JSON; -import org.springframework.ai.models.midjourney.MidjourneyMessage; -import org.springframework.ai.models.midjourney.constants.MidjourneyGennerateStatusEnum; -import org.springframework.ai.models.midjourney.webSocket.MidjourneyMessageHandler; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyOperationsVO; -import cn.iocoder.yudao.module.ai.convert.AiImageConvert; -import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; -import cn.iocoder.yudao.module.ai.dal.mysql.AiImageMapper; -import cn.iocoder.yudao.module.ai.enums.AiImageDrawingStatusEnum; -import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -/** - * yudao message handler - * - * @author fansili - * @time 2024/4/29 14:34 - * @since 1.0 - */ -@Component -@Slf4j -@AllArgsConstructor -public class YuDaoMidjourneyMessageHandler implements MidjourneyMessageHandler { - - private final AiImageMapper aiImageMapper; - - @Override - public void messageHandler(MidjourneyMessage midjourneyMessage) { - log.info("yudao-midjourney-midjourney-message-handler {}", JSON.toJSONString(midjourneyMessage)); - if (midjourneyMessage.getContent() != null) { - log.info("进度id {} 状态 {} 进度 {}", - midjourneyMessage.getNonce(), - midjourneyMessage.getGenerateStatus(), - midjourneyMessage.getContent().getProgress()); - } - // - updateImage(midjourneyMessage); - } - - private void updateImage(MidjourneyMessage midjourneyMessage) { - // Nonce 不存在不更新 - if (StrUtil.isBlank(midjourneyMessage.getNonce())) { - return; - } - // 根据 Embeds 来判断是否异常 - if (CollUtil.isEmpty(midjourneyMessage.getEmbeds())) { - successHandler(midjourneyMessage); - } else { - errorHandler(midjourneyMessage); - } - } - - private void errorHandler(MidjourneyMessage midjourneyMessage) { - // image 编号 - Long nonceId = Long.valueOf(midjourneyMessage.getNonce()); - // 获取 error message - String errorMessage = getErrorMessage(midjourneyMessage); - aiImageMapper.updateByMjNonce(nonceId, - new AiImageDO() - .setDrawingErrorMessage(errorMessage) - .setDrawingStatus(AiImageDrawingStatusEnum.FAIL.getStatus()) - ); - } - - private String getErrorMessage(MidjourneyMessage midjourneyMessage) { - StringBuilder errorMessage = new StringBuilder(); - for (MidjourneyMessage.Embed embed : midjourneyMessage.getEmbeds()) { - errorMessage.append(embed.getDescription()); - } - return errorMessage.toString(); - } - - private void successHandler(MidjourneyMessage midjourneyMessage) { - // 获取id - Long nonceId = Long.valueOf(midjourneyMessage.getNonce()); - // TODO @芋艿 这个地方有问题,不能根据 nonce来更新,不返回这个信息(别人获取了 image-xxx-xx 后面一段hash,由于没有mj账号测试,暂不清楚。) - // 获取生成 url - String imageUrl = null; - if (CollUtil.isNotEmpty(midjourneyMessage.getAttachments())) { - imageUrl = midjourneyMessage.getAttachments().get(0).getUrl(); - } - // 转换状态 - AiImageDrawingStatusEnum drawingStatusEnum = null; - String generateStatus = midjourneyMessage.getGenerateStatus(); - if (MidjourneyGennerateStatusEnum.COMPLETED.getStatus().equals(generateStatus)) { - drawingStatusEnum = AiImageDrawingStatusEnum.COMPLETE; - } else if (MidjourneyGennerateStatusEnum.IN_PROGRESS.getStatus().equals(generateStatus)) { - drawingStatusEnum = AiImageDrawingStatusEnum.IN_PROGRESS; - } else if (MidjourneyGennerateStatusEnum.WAITING.getStatus().equals(generateStatus)) { - drawingStatusEnum = AiImageDrawingStatusEnum.WAITING; - } - // 获取 midjourneyOperations - List midjourneyOperations = getMidjourneyOperationsList(midjourneyMessage); - // 更新数据库 - aiImageMapper.updateByMjNonce(nonceId, - new AiImageDO() - .setDrawingImageUrl(imageUrl) - .setDrawingStatus(drawingStatusEnum == null ? null : drawingStatusEnum.getStatus()) - .setMjNonceId(midjourneyMessage.getId()) - .setMjOperations(JsonUtils.toJsonString(midjourneyOperations)) - ); - } - - private List getMidjourneyOperationsList(MidjourneyMessage midjourneyMessage) { - // 为空直接返回 - if (CollUtil.isEmpty(midjourneyMessage.getComponents())) { - return Collections.emptyList(); - } - // 将 component 转成 AiImageMidjourneyOperationsVO - return midjourneyMessage.getComponents().stream() - .map(componentType -> componentType.getComponents().stream() - .map(AiImageConvert.INSTANCE::convertAiImageMidjourneyOperationsVO) - .collect(Collectors.toList())) - .toList().stream().flatMap(List::stream).toList(); - } -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelService.java index 08f0110..f83ac73 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelService.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatMode import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; import jakarta.validation.Valid; +import java.util.Collection; import java.util.List; import java.util.Set; @@ -79,13 +80,13 @@ public interface AiChatModelService { * @param status 状态 * @return 聊天模型列表 */ - List getChatModelList(Integer status); + List getChatModelListByStatus(Integer status); /** - * 获取 - 根据多个 ids 获取 + * 获得聊天模型列表 * - * @param modalIds - * @return + * @param ids 编号数组 + * @return 模型列表 */ - List getModalByIds(Set modalIds); + List getChatModelList(Collection ids); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java index 59298f9..31d048c 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java @@ -7,15 +7,14 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelSaveReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; -import cn.iocoder.yudao.module.ai.dal.mysql.AiChatModelMapper; +import cn.iocoder.yudao.module.ai.dal.mysql.model.AiChatModelMapper; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; +import java.util.Collection; import java.util.List; -import java.util.Set; - import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.*; @@ -103,13 +102,13 @@ public class AiChatModelServiceImpl implements AiChatModelService { } @Override - public List getChatModelList(Integer status) { + public List getChatModelListByStatus(Integer status) { return chatModelMapper.selectList(status); } @Override - public List getModalByIds(Set modalIds) { - return chatModelMapper.selectByIds(modalIds); + public List getChatModelList(Collection ids) { + return chatModelMapper.selectBatchIds(ids); } } \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleServiceImpl.java index 8313e12..faee09d 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleServiceImpl.java @@ -10,7 +10,7 @@ import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleP import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveMyReqVO; import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; -import cn.iocoder.yudao.module.ai.dal.mysql.AiChatRoleMapper; +import cn.iocoder.yudao.module.ai.dal.mysql.model.AiChatRoleMapper; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiClientFactoryImpl.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiClientFactoryImpl.java index 44a76dd..ba496ad 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiClientFactoryImpl.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiClientFactoryImpl.java @@ -20,6 +20,7 @@ import cn.iocoder.yudao.framework.ai.core.model.xinghuo.api.XingHuoApi; import cn.iocoder.yudao.framework.ai.core.model.yiyan.YiYanChatClient; import cn.iocoder.yudao.framework.ai.core.model.yiyan.YiYanChatOptions; import cn.iocoder.yudao.framework.ai.core.model.yiyan.api.YiYanApi; +import com.google.cloud.vertexai.VertexAI; import org.springframework.ai.autoconfigure.ollama.OllamaAutoConfiguration; import org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration; import org.springframework.ai.chat.StreamingChatClient; @@ -31,6 +32,8 @@ import org.springframework.ai.openai.OpenAiChatClient; import org.springframework.ai.openai.OpenAiChatOptions; import org.springframework.ai.openai.api.ApiUtils; import org.springframework.ai.openai.api.OpenAiApi; +import org.springframework.ai.vertexai.gemini.VertexAiGeminiChatClient; +import org.springframework.ai.vertexai.gemini.VertexAiGeminiChatOptions; import java.util.List; @@ -57,8 +60,8 @@ public class AiClientFactoryImpl implements AiClientFactory { return buildXingHuoChatClient(apiKey); case QIAN_WEN: return buildQianWenChatClient(apiKey); -// case GEMIR: -// return buildGoogleGemir(apiKey); + case GEMIR: + return buildGoogleGemir(apiKey); default: throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); } @@ -165,24 +168,13 @@ public class AiClientFactoryImpl implements AiClientFactory { QianWenApi qianWenApi = new QianWenApi(key, QianWenChatModal.QWEN_72B_CHAT); return new QianWenChatClient(qianWenApi); } -// -// private static VertexAiGeminiChatClient buildGoogleGemir(String key) { -// List keys = StrUtil.split(key, '|'); -// Assert.equals(keys.size(), 2, "VertexAiGeminiChatClient 的密钥需要 (projectId|location) 格式"); -//// VertexAiGeminiConnectionProperties connectionProperties = new VertexAiGeminiConnectionProperties(); -//// connectionProperties.setApiKey("AIzaSyBpe376HTA8uPKJN_OJTh7MEO3v6LMqfXU"); -//// -//// GoogleCredentials credentials = GoogleCredentials.fromStream(connectionProperties.getCredentialsUri().getInputStream()); -// // todo @芋艿 google gemini 没找到对于初始化 client 方式,文档中说是用过 GoogleCredentials 来初始化凭证 -// // api-key: AIzaSyBpe376HTA8uPKJN_OJTh7MEO3v6LMqfXU -// VertexAI vertexApi = new VertexAI( -// "skilled-snow-409401", -// "us-central1" -// ); -// return new VertexAiGeminiChatClient(vertexApi, -// VertexAiGeminiChatOptions.builder() -// .withTemperature(0.4F) -// .withModel(VertexAiGeminiChatClient.ChatModel.GEMINI_PRO.getValue()) -// .build()); -// } + + + private static VertexAiGeminiChatClient buildGoogleGemir(String key) { + List keys = StrUtil.split(key, '|'); + Assert.equals(keys.size(), 2, "VertexAiGeminiChatClient 的密钥需要 (projectId|location) 格式"); + VertexAI vertexApi = new VertexAI(keys.get(0), keys.get(1)); + return new VertexAiGeminiChatClient(vertexApi); + } + } diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/tongyi/QianWenChatClient.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/tongyi/QianWenChatClient.java index 63f3af7..02a25ce 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/tongyi/QianWenChatClient.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/tongyi/QianWenChatClient.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.framework.ai.core.model.tongyi; +import cn.hutool.core.util.NumberUtil; import cn.iocoder.yudao.framework.ai.core.exception.ChatException; import cn.iocoder.yudao.framework.ai.core.model.tongyi.api.QianWenApi; import org.springframework.ai.chat.*; -- libgit2 0.21.4