From c21b5f76790e9cc05bfa0dd2545be057e96e1f94 Mon Sep 17 00:00:00 2001 From: cherishsince Date: Mon, 6 May 2024 16:32:47 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E8=B0=83=E6=95=B4=E3=80=91=E6=A0=B9?= =?UTF-8?q?=E6=8D=AE=E6=8E=A5=E5=8F=A3=E8=AE=BE=E8=AE=A1=E8=B0=83=E6=95=B4?= =?UTF-8?q?=EF=BC=8Cai=20model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/ai/ErrorCodeConstants.java | 25 +++--- .../module/ai/enums/AiChatModalTypeEnum.java | 11 +++ .../ai/controller/AiChatModalController.java | 2 +- .../ai/dal/dataobject/AiChatModalDO.java | 20 ++--- .../ai/dal/vo/AiChatModalChatConfigVO.java | 35 ++++++++ .../module/ai/dal/vo/AiChatModalConfigVO.java | 28 +++++++ .../ai/dal/vo/AiChatModalDallConfigVO.java | 41 ++++++++++ .../dal/vo/AiChatModalMidjourneyConfigVO.java | 16 ++++ .../AiChatModelConfigTypeHandler.java | 75 +++++++++++++++++ .../service/impl/AiChatModalServiceImpl.java | 80 ++++++++++++++++++- .../ai/service/impl/AiImageServiceImpl.java | 5 +- .../yudao/module/ai/vo/AiChatModalAddReq.java | 22 +++-- .../module/ai/vo/AiChatModalListRes.java | 42 +++++----- .../src/main/resources/http/chat-modal.http | 34 ++++++-- .../yudao/framework/ai/AiPlatformEnum.java | 18 +++++ .../ai/config/YudaoAiProperties.java | 4 +- 16 files changed, 392 insertions(+), 66 deletions(-) create mode 100644 yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatModalTypeEnum.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalChatConfigVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalConfigVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalDallConfigVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalMidjourneyConfigVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/mapper/typeHandler/AiChatModelConfigTypeHandler.java diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java index 5839f11f1..12652cb0b 100644 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java @@ -11,24 +11,27 @@ public interface ErrorCodeConstants { // ========== 模块 ai 错误码区间 [1-022-000-000 ~ 1-023-000-000) ========== - // TODO @fansili:1)类注释不太对;2)中英文之间,有个空格;例如说 AI 模型 - ErrorCode AI_MODULE_NOT_SUPPORTED = new ErrorCode(1_022_000_000, "AI模型暂不支持!"); + // chat + ErrorCode AI_MODULE_NOT_SUPPORTED = new ErrorCode(1_022_000_000, "AI 模型暂不支持!"); ErrorCode AI_CHAT_ROLE_NOT_EXISTENT = new ErrorCode(1_022_000_001, "AI Role 不存在!");; + ErrorCode AI_CHAT_CONTINUE_CONVERSATION_ID_NOT_NULL = new ErrorCode(1_022_000_002, "chat 继续对话,对话 id 不能为空!");; + ErrorCode AI_CHAT_CONTINUE_NOT_EXIST = new ErrorCode(1_022_000_020, "chat 对话不存在!"); + ErrorCode AI_CHAT_CONVERSATION_NOT_YOURS = new ErrorCode(1_022_000_021, "这条 chat 对话不是你的!"); - - ErrorCode AI_CHAT_CONTINUE_CONVERSATION_ID_NOT_NULL = new ErrorCode(1_022_000_002, "chat 继续对话,对话id不能为空!");; - - - - ErrorCode AI_CHAT_CONTINUE_NOT_EXIST = new ErrorCode(1_022_000_020, "chat对话不存在!"); - ErrorCode AI_CHAT_CONVERSATION_NOT_YOURS = new ErrorCode(1_022_000_021, "这条chat对话不是你的!"); + // midjourney ErrorCode AI_MIDJOURNEY_IMAGINE_FAIL = new ErrorCode(1_022_000_040, "midjourney imagine 操作失败!"); - ErrorCode AI_CHAT_ROLE_NOT_EXIST = new ErrorCode(1_022_000_060, "chatRole不存在!"); + // role - ErrorCode AI_MODAL_NOT_EXIST = new ErrorCode(1_022_000_080, "ai模型不存在!"); + ErrorCode AI_CHAT_ROLE_NOT_EXIST = new ErrorCode(1_022_000_060, "chatRole 不存在!"); + // modal + + ErrorCode AI_MODAL_NOT_EXIST = new ErrorCode(1_022_000_080, "AI 模型不存在!"); + ErrorCode AI_MODAL_CONFIG_PARAMS_INCORRECT = new ErrorCode(1_022_000_081, "AI 模型 config 参数不正确! {} "); + ErrorCode AI_MODAL_NOT_SUPPORTED_MODAL = new ErrorCode(1_022_000_082, "AI 模型不支持的 modal! {} "); + ErrorCode AI_MODAL_PLATFORM_PARAMS_INCORRECT = new ErrorCode(1_022_000_083, "AI 平台参数不正确! {} "); } diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatModalTypeEnum.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatModalTypeEnum.java new file mode 100644 index 000000000..0cf6c6ab1 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatModalTypeEnum.java @@ -0,0 +1,11 @@ +package cn.iocoder.yudao.module.ai.enums; + +/** + * 枚举 + * + * @author fansili + * @time 2024/5/6 11:48 + * @since 1.0 + */ +public enum AiChatModalTypeEnum { +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/AiChatModalController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/AiChatModalController.java index 9530f23b9..cbbb23049 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/AiChatModalController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/AiChatModalController.java @@ -30,7 +30,7 @@ public class AiChatModalController { private final AiChatModalService aiChatModalService; - @Operation(summary = "ai模型 - 模型照片上传") + @Operation(summary = "ai模型 - 模型列表") @GetMapping("/modal/list") public PageResult list(@ModelAttribute AiChatModalListReq req) { return aiChatModalService.list(req); diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/AiChatModalDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/AiChatModalDO.java index c417e5a44..43175d349 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/AiChatModalDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/AiChatModalDO.java @@ -22,27 +22,23 @@ public class AiChatModalDO extends BaseDO { */ private Long id; /** - * 模型key + * 名字 */ - private String modelKey; + private String name; /** - * 模型类型 参考:{@link cn.iocoder.yudao.framework.ai.AiPlatformEnum} - */ - private String modelPlatform; - /** - * 模型类型 + * 类型 * {@link cn.iocoder.yudao.framework.ai.chatyiyan.YiYanChatModel} * {@link cn.iocoder.yudao.framework.ai.chatxinghuo.XingHuoChatModel} */ - private String modelType; + private String modal; /** - * 模型名字 + * 平台 参考:{@link cn.iocoder.yudao.framework.ai.AiPlatformEnum} */ - private String modelName; + private String platform; /** - * 模型照片 + * 图片地址 */ - private String modalImage; + private String imageUrl; /** * 禁用 0、正常 1、禁用 */ 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 new file mode 100644 index 000000000..79cce7ad4 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalChatConfigVO.java @@ -0,0 +1,35 @@ +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 new file mode 100644 index 000000000..1b6e957d0 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalConfigVO.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.ai.dal.vo; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * modal config + * + * @author fansili + * @time 2024/5/6 15:06 + * @since 1.0 + */ +@Data +@Accessors(chain = true) +public class AiChatModalConfigVO { + + /** + * 模型平台 (冗余,方便类型转换) + * 参考:{@link cn.iocoder.yudao.framework.ai.AiPlatformEnum} + */ + private String platform; + /** + * 模型类型(冗余,方便类型转换) + * {@link cn.iocoder.yudao.framework.ai.chatyiyan.YiYanChatModel} + * {@link cn.iocoder.yudao.framework.ai.chatxinghuo.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 new file mode 100644 index 000000000..c7dce40e6 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalDallConfigVO.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.ai.dal.vo; + +import cn.iocoder.yudao.framework.ai.imageopenai.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 new file mode 100644 index 000000000..543c58705 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/vo/AiChatModalMidjourneyConfigVO.java @@ -0,0 +1,16 @@ +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/mapper/typeHandler/AiChatModelConfigTypeHandler.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/mapper/typeHandler/AiChatModelConfigTypeHandler.java new file mode 100644 index 000000000..b1f54eaac --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/mapper/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/impl/AiChatModalServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatModalServiceImpl.java index 9e4b6e41d..cc7f505b1 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatModalServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatModalServiceImpl.java @@ -1,23 +1,34 @@ package cn.iocoder.yudao.module.ai.service.impl; import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.validation.ValidationUtil; +import cn.iocoder.yudao.framework.ai.AiPlatformEnum; +import cn.iocoder.yudao.framework.ai.chatqianwen.QianWenChatModal; +import cn.iocoder.yudao.framework.ai.chatxinghuo.XingHuoChatModel; +import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanChatModel; 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.module.ai.ErrorCodeConstants; import cn.iocoder.yudao.module.ai.convert.AiChatModalConvert; import cn.iocoder.yudao.module.ai.dal.dataobject.AiChatModalDO; +import cn.iocoder.yudao.module.ai.dal.vo.AiChatModalChatConfigVO; +import cn.iocoder.yudao.module.ai.dal.vo.AiChatModalConfigVO; +import cn.iocoder.yudao.module.ai.dal.vo.AiChatModalDallConfigVO; import cn.iocoder.yudao.module.ai.enums.AiChatModalDisableEnum; import cn.iocoder.yudao.module.ai.mapper.AiChatModalMapper; import cn.iocoder.yudao.module.ai.service.AiChatModalService; import cn.iocoder.yudao.module.ai.vo.AiChatModalAddReq; import cn.iocoder.yudao.module.ai.vo.AiChatModalListReq; import cn.iocoder.yudao.module.ai.vo.AiChatModalListRes; +import jakarta.validation.ConstraintViolation; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.List; +import java.util.Set; /** * ai 模型 @@ -40,7 +51,7 @@ public class AiChatModalServiceImpl implements AiChatModalService { queryWrapperX.eq(AiChatModalDO::getDisable, AiChatModalDisableEnum.NO.getValue()); // search if (!StrUtil.isBlank(req.getSearch())) { - queryWrapperX.like(AiChatModalDO::getModelName, req.getSearch().trim()); + queryWrapperX.like(AiChatModalDO::getName, req.getSearch().trim()); } // 默认排序 queryWrapperX.orderByDesc(AiChatModalDO::getId); @@ -53,21 +64,38 @@ public class AiChatModalServiceImpl implements AiChatModalService { @Override public void add(AiChatModalAddReq req) { + // 校验 platform、type + validatePlatform(req.getPlatform()); + validateModal(req.getPlatform(), req.getModal()); + // 转换config + AiChatModalConfigVO aiChatModalConfigVO = convertConfig(req); + // 校验 modal config + validateModalConfig(aiChatModalConfigVO); // 转换 do AiChatModalDO insertChatModalDO = AiChatModalConvert.INSTANCE.convertAiChatModalDO(req); - // + // 设置默认属性 insertChatModalDO.setDisable(AiChatModalDisableEnum.NO.getValue()); + insertChatModalDO.setConfig(JsonUtils.toJsonString(aiChatModalConfigVO)); // 保存数据库 aiChatModalMapper.insert(insertChatModalDO); } @Override public void update(Long id, AiChatModalAddReq req) { + // 校验 platform、type + validatePlatform(req.getPlatform()); + validateModal(req.getPlatform(), req.getModal()); + // 转换config + AiChatModalConfigVO aiChatModalConfigVO = convertConfig(req); + // 校验 modal config + validateModalConfig(aiChatModalConfigVO); + // 校验模型是否存在 validateChatModalExists(id); // 转换 updateChatModalDO AiChatModalDO updateChatModalDO = AiChatModalConvert.INSTANCE.convertAiChatModalDO(req); updateChatModalDO.setId(id); + updateChatModalDO.setConfig(JsonUtils.toJsonString(aiChatModalConfigVO)); // 更新数据库 aiChatModalMapper.updateById(updateChatModalDO); } @@ -86,4 +114,52 @@ public class AiChatModalServiceImpl implements AiChatModalService { throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MODAL_NOT_EXIST); } } + + private void validateModal(String platform, String modal) { + AiPlatformEnum platformEnum = AiPlatformEnum.valueOfPlatform(platform); + try { + if (AiPlatformEnum.QIAN_WEN == platformEnum) { + QianWenChatModal.valueOfModel(modal); + } else if (AiPlatformEnum.XING_HUO == platformEnum) { + XingHuoChatModel.valueOfModel(modal); + } else if (AiPlatformEnum.YI_YAN == platformEnum) { + YiYanChatModel.valueOfModel(modal); + } else { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MODAL_NOT_SUPPORTED_MODAL, platform); + } + } catch (IllegalArgumentException e) { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MODAL_CONFIG_PARAMS_INCORRECT, e.getMessage()); + } + } + + private void validatePlatform(String platform) { + try { + AiPlatformEnum.valueOfPlatform(platform); + } catch (IllegalArgumentException e) { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MODAL_PLATFORM_PARAMS_INCORRECT, e.getMessage()); + } + } + + private void validateModalConfig(AiChatModalConfigVO aiChatModalConfigVO) { + Set> validate = ValidationUtil.validate(aiChatModalConfigVO); + for (ConstraintViolation constraintViolation : validate) { + throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MODAL_CONFIG_PARAMS_INCORRECT, constraintViolation.getMessage()); + } + } + + private static AiChatModalConfigVO convertConfig(AiChatModalAddReq req) { + AiPlatformEnum platformEnum = AiPlatformEnum.valueOfPlatform(req.getPlatform()); + AiChatModalConfigVO resVo = null; + if (AiPlatformEnum.CHAT_PLATFORM_LIST.contains(platformEnum)) { + resVo = JsonUtils.parseObject(JsonUtils.toJsonString(req.getConfig()), AiChatModalChatConfigVO.class); + } else if (AiPlatformEnum.OPEN_AI_DALL == platformEnum) { + resVo = JsonUtils.parseObject(JsonUtils.toJsonString(req.getConfig()), AiChatModalDallConfigVO.class); + } + if (resVo == null) { + throw new IllegalArgumentException("ai模型中config不能转换! json: " + req.getConfig()); + } + resVo.setType(req.getModal()); + resVo.setPlatform(req.getPlatform()); + return resVo; + } } 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 index 73e97c9c5..fb5ebf690 100644 --- 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 @@ -5,9 +5,9 @@ import cn.iocoder.yudao.framework.ai.image.ImageGeneration; import cn.iocoder.yudao.framework.ai.image.ImagePrompt; import cn.iocoder.yudao.framework.ai.image.ImageResponse; import cn.iocoder.yudao.framework.ai.imageopenai.OpenAiImageClient; -import cn.iocoder.yudao.framework.ai.imageopenai.OpenAiImageModelEnum; +import cn.iocoder.yudao.framework.ai.imageopenai.enums.OpenAiImageModelEnum; import cn.iocoder.yudao.framework.ai.imageopenai.OpenAiImageOptions; -import cn.iocoder.yudao.framework.ai.imageopenai.OpenAiImageStyleEnum; +import cn.iocoder.yudao.framework.ai.imageopenai.enums.OpenAiImageStyleEnum; import cn.iocoder.yudao.framework.ai.midjourney.api.MidjourneyInteractionsApi; import cn.iocoder.yudao.framework.ai.midjourney.webSocket.MidjourneyWebSocketStarter; import cn.iocoder.yudao.framework.ai.midjourney.webSocket.WssNotify; @@ -21,7 +21,6 @@ import cn.iocoder.yudao.module.ai.mapper.AiImageMapper; import cn.iocoder.yudao.module.ai.service.AiImageService; import cn.iocoder.yudao.module.ai.vo.AiImageDallDrawingReq; import cn.iocoder.yudao.module.ai.vo.AiImageMidjourneyReq; -import cn.iocoder.yudao.module.ai.vo.AiImageMidjourneyRes; import jakarta.annotation.PostConstruct; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/vo/AiChatModalAddReq.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/vo/AiChatModalAddReq.java index b20c92d5b..4145e2cd4 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/vo/AiChatModalAddReq.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/vo/AiChatModalAddReq.java @@ -6,6 +6,8 @@ import jakarta.validation.constraints.Size; import lombok.Data; import lombok.experimental.Accessors; +import java.util.Map; + /** * ai chat modal * @@ -19,18 +21,24 @@ public class AiChatModalAddReq { @NotNull @Schema(description = "模型名字") - @Size(max = 60, message = "模型名字最大60") - private String modelName; + @Size(max = 60, message = "模型名字最大60个字符") + private String name; + + @NotNull + @Size(max = 32, message = "模型平台最大32个字符") + @Schema(description = "模型平台 参考 AiPlatformEnum") + private String platform; @NotNull @Schema(description = "模型类型(qianwen、yiyan、xinghuo、openai)") - @Size(max = 32, message = "模型类型最大32") - private String modelType; + @Size(max = 32, message = "模型类型最大32个字符") + private String modal; @Schema(description = "模型照片") - private String modalImage; + @Size(max = 256, message = "模型照片地址最大256个字符") + private String imageUrl; @Schema(description = "模型配置JSON") - private String modelConfig; - +// @Size(max = 1024, message = "模型配置最大1024个字符") + private Map config; } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/vo/AiChatModalListRes.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/vo/AiChatModalListRes.java index 3d1701560..d05ed7d0d 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/vo/AiChatModalListRes.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/vo/AiChatModalListRes.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.module.ai.vo; +import cn.iocoder.yudao.module.ai.dal.dataobject.AiChatModalDO; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.experimental.Accessors; @@ -13,29 +15,25 @@ import lombok.experimental.Accessors; @Data @Accessors(chain = true) public class AiChatModalListRes { - /** - * 编号 - */ + + @Schema(description = "id") private Long id; - /** - * 模型名字 - */ - private String modelName; - /** - * 模型类型(qianwen、yiyan、xinghuo、openai) - */ - private String modelType; - /** - * 模型照片 - */ - private String modalImage; - /** - * 模型配置JSON - */ - private String modelConfig; - /** - * 禁用 0、正常 1、禁用 - */ + + @Schema(description = "模型平台 参考 AiPlatformEnum") + private String platform; + + @Schema(description = "模型类型 参考 YiYanChatModel、XingHuoChatModel") + private String modal; + + @Schema(description = "模型名字") + private String name; + + @Schema(description = "模型照片") + private String image; + + @Schema(description = "禁用 0、正常 1、禁用") private Integer disable; + @Schema(description = "modal 配置") + private String config; } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/resources/http/chat-modal.http b/yudao-module-ai/yudao-module-ai-biz/src/main/resources/http/chat-modal.http index aa2949920..50c89908c 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/resources/http/chat-modal.http +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/resources/http/chat-modal.http @@ -1,19 +1,41 @@ - ### chat call GET {{baseUrl}}/ai/chat/modal/list Authorization: {{token}} - ### chat call PUT {{baseUrl}}/ai/chat/modal Content-Type: application/json Authorization: {{token}} { - "modelName": "小红书Ai写作大模型", - "modelType": "yiyan", - "modalImage": "", - "modelConfig": "" + "name": "小红书Ai写作大模型-plus", + "modal": "ERNIE-3.5-8K", + "platform": "yiyan", + "imageUrl": "", + "config": { + "topK": 0.6, + "topP": 0.6, + "temperature": 0.86, + "maxTokens": 2048 + } +} + +### chat call +POST {{baseUrl}}/ai/chat/modal/7 +Content-Type: application/json +Authorization: {{token}} + +{ + "name": "小红书Ai写作大模型-plus", + "modal": "ERNIE-3.5-8K", + "platform": "yiyan", + "imageUrl": "", + "config": { + "topK": 0.6, + "topP": 0.6, + "temperature": 0.86, + "maxTokens": 2048 + } } diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/AiPlatformEnum.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/AiPlatformEnum.java index 717ced538..752cb042d 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/AiPlatformEnum.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/AiPlatformEnum.java @@ -1,8 +1,11 @@ package cn.iocoder.yudao.framework.ai; +import com.google.common.collect.Lists; import lombok.AllArgsConstructor; import lombok.Getter; +import java.util.List; + /** * ai 模型平台 * @@ -17,12 +20,27 @@ public enum AiPlatformEnum { YI_YAN("yiyan", "一言"), QIAN_WEN("qianwen", "千问"), XING_HUO("xinghuo", "星火"), + OPEN_AI("openai", "openAi"), + OPEN_AI_DALL("dall", "dall"), + MIDJOURNEY("midjourney", "midjourney"), ; private String platform; private String name; + public static List CHAT_PLATFORM_LIST = Lists.newArrayList( + AiPlatformEnum.YI_YAN, + AiPlatformEnum.QIAN_WEN, + AiPlatformEnum.XING_HUO, + AiPlatformEnum.OPEN_AI + ); + + public static List IMAGE_PLATFORM_LIST = Lists.newArrayList( + AiPlatformEnum.OPEN_AI_DALL, + AiPlatformEnum.MIDJOURNEY + ); + public static AiPlatformEnum valueOfPlatform(String platform) { for (AiPlatformEnum itemEnum : AiPlatformEnum.values()) { if (itemEnum.getPlatform().equals(platform)) { diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiProperties.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiProperties.java index f9dcae04a..57ef69ef6 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiProperties.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiProperties.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.framework.ai.config; import cn.iocoder.yudao.framework.ai.AiPlatformEnum; import cn.iocoder.yudao.framework.ai.chatxinghuo.XingHuoChatModel; import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanChatModel; -import cn.iocoder.yudao.framework.ai.imageopenai.OpenAiImageModelEnum; -import cn.iocoder.yudao.framework.ai.imageopenai.OpenAiImageStyleEnum; +import cn.iocoder.yudao.framework.ai.imageopenai.enums.OpenAiImageModelEnum; +import cn.iocoder.yudao.framework.ai.imageopenai.enums.OpenAiImageStyleEnum; import lombok.Data; import lombok.experimental.Accessors; import org.springframework.boot.context.properties.ConfigurationProperties;