From 6856f5f192d4c726a67ef3b45c9e744a49c735ae Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 1 Jun 2024 14:16:37 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91AI=EF=BC=9A?= =?UTF-8?q?=E8=BF=9B=E4=B8=80=E6=AD=A5=E7=BB=9F=E4=B8=80=20DALL=E3=80=81SD?= =?UTF-8?q?=20=E7=9A=84=E7=BB=98=E5=88=B6=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/ai/AiCommonConstants.java | 21 ----- .../yudao/module/ai/api/package-info.java | 4 + .../ai/enums/AiChatConversationTypeEnum.java | 37 -------- .../ai/enums/AiChatRoleCategoryEnum.java | 37 -------- .../ai/enums/AiImagePublicStatusEnum.java | 36 -------- .../ai/{ => enums}/ErrorCodeConstants.java | 2 +- .../enums/{ => image}/AiImageStatusEnum.java | 4 +- .../ai/enums/{ => model}/AiModelEnum.java | 2 +- .../admin/image/AiImageController.http} | 30 ++++-- .../admin/image/AiImageController.java | 17 ++-- .../admin/image/vo/AiImageDallReqVO.java | 41 --------- .../admin/image/vo/AiImageDrawReqVO.java | 52 +++++++++++ .../vo/AiImageMidjourneyOperateReqVO.java | 31 ------- .../admin/image/vo/AiImageRespVO.java | 59 ++++-------- .../dataobject/chat/AiChatConversationDO.java | 3 +- .../dal/dataobject/chat/AiChatMessageDO.java | 2 +- .../ai/dal/dataobject/image/AiImageDO.java | 92 +++++++++++-------- .../chat/AiChatConversationServiceImpl.java | 4 +- .../chat/AiChatMessageServiceImpl.java | 6 +- .../ai/service/image/AiImageService.java | 10 +- .../ai/service/image/AiImageServiceImpl.java | 73 ++++++--------- .../YuDaoMidjourneyMessageHandler.java | 5 +- .../vo/AiChatModalChatConfigVO.java | 35 ------- .../vo/AiChatModalConfigVO.java | 31 ------- .../vo/AiChatModalDallConfigVO.java | 41 --------- .../vo/AiChatModalMidjourneyConfigVO.java | 16 ---- .../ai/service/model/AiApiKeyServiceImpl.java | 2 +- .../service/model/AiChatModelServiceImpl.java | 2 +- .../service/model/AiChatRoleServiceImpl.java | 2 +- .../ai/config/YudaoAiProperties.java | 24 +---- .../ai/core/enums/AiPlatformEnum.java | 1 - .../ai/core/enums/OpenAiImageModelEnum.java | 37 -------- .../ai/core/enums/OpenAiImageStyleEnum.java | 38 -------- .../ai/core/factory/AiClientFactoryImpl.java | 10 +- 34 files changed, 220 insertions(+), 587 deletions(-) delete mode 100644 yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/AiCommonConstants.java create mode 100644 yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/api/package-info.java delete mode 100644 yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatConversationTypeEnum.java delete mode 100644 yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleCategoryEnum.java delete mode 100644 yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiImagePublicStatusEnum.java rename yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/{ => enums}/ErrorCodeConstants.java (98%) rename yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/{ => image}/AiImageStatusEnum.java (90%) rename yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/{ => model}/AiModelEnum.java (98%) rename yudao-module-ai/yudao-module-ai-biz/src/main/{resources/http/image.http => java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.http} (52%) delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageDallReqVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageDrawReqVO.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageMidjourneyOperateReqVO.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalChatConfigVO.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalConfigVO.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalDallConfigVO.java delete 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-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/OpenAiImageModelEnum.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/OpenAiImageStyleEnum.java diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/AiCommonConstants.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/AiCommonConstants.java deleted file mode 100644 index 01b57733e..000000000 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/AiCommonConstants.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.ai; - -/** - * ai 常用的常量 - * - * @author fansili - * @time 2024/5/7 09:29 - * @since 1.0 - */ -public class AiCommonConstants { - - /** - * 绘画 request - style - */ - public static final String DRAW_REQ_KEY_STYLE = "style"; - /** - * dall size - 模板(1024x1024) - */ - public static final String DALL_SIZE_TEMPLATE = "%sx%s"; - -} diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/api/package-info.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/api/package-info.java new file mode 100644 index 000000000..4f94f23f8 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/api/package-info.java @@ -0,0 +1,4 @@ +/** + * 占位,没有特别的作用 + */ +package cn.iocoder.yudao.module.ai.api; \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatConversationTypeEnum.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatConversationTypeEnum.java deleted file mode 100644 index a91bed98d..000000000 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatConversationTypeEnum.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.ai.enums; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 对话类型 - * 创建对话、继续对话 - * - * @author fansili - * @time 2024/4/14 18:15 - * @since 1.0 - */ -@AllArgsConstructor -@Getter -public enum AiChatConversationTypeEnum { - - // roleChat、userChat - ROLE_CHAT("roleChat", "角色对话"), - USER_CHAT("userChat", "用户对话"), - - ; - - private String type; - - private String name; - - public static AiChatConversationTypeEnum valueOfType(String type) { - for (AiChatConversationTypeEnum itemEnum : AiChatConversationTypeEnum.values()) { - if (itemEnum.getType().equals(type)) { - return itemEnum; - } - } - throw new IllegalArgumentException("Invalid MessageType value: " + type); - } - -} diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleCategoryEnum.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleCategoryEnum.java deleted file mode 100644 index 3ca923de9..000000000 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleCategoryEnum.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.ai.enums; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 聊天role 分类 - * - * @author fansili - * @time 2024/4/24 16:41 - * @since 1.0 - */ -@AllArgsConstructor -@Getter -public enum AiChatRoleCategoryEnum { - - WRITING("writing", "写作"), - - ENTERTAINMENT("entertainment", "娱乐"), - - ; - - - private String category; - - private String name; - - - public static AiChatRoleCategoryEnum valueOfCategory(String category) { - for (AiChatRoleCategoryEnum itemEnum : AiChatRoleCategoryEnum.values()) { - if (itemEnum.getCategory().equals(category)) { - return itemEnum; - } - } - throw new IllegalArgumentException("Invalid MessageType value: " + category); - } -} diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiImagePublicStatusEnum.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiImagePublicStatusEnum.java deleted file mode 100644 index 3a7762bb7..000000000 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiImagePublicStatusEnum.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.ai.enums; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * ai绘画 public 状态 - * - * @author fansili - * @time 2024/4/28 17:05 - * @since 1.0 - */ -@AllArgsConstructor -@Getter -public enum AiImagePublicStatusEnum { - - PRIVATE("private", "私有"), - PUBLIC("public", "公开"), - - ; - - // TODO @fan:final 一下 - private final String status; - - private final String name; - - - public static AiImagePublicStatusEnum valueOfStatus(String status) { - for (AiImagePublicStatusEnum itemEnum : AiImagePublicStatusEnum.values()) { - if (itemEnum.getStatus().equals(status)) { - return itemEnum; - } - } - throw new IllegalArgumentException("Invalid MessageType value: " + status); - } -} 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/enums/ErrorCodeConstants.java similarity index 98% rename from yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java rename to yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java index f0833b71f..be850e6e7 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/enums/ErrorCodeConstants.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.ai; +package cn.iocoder.yudao.module.ai.enums; import cn.iocoder.yudao.framework.common.exception.ErrorCode; diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiImageStatusEnum.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/image/AiImageStatusEnum.java similarity index 90% rename from yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiImageStatusEnum.java rename to yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/image/AiImageStatusEnum.java index 39df7b1a8..4fd942b39 100644 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiImageStatusEnum.java +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/image/AiImageStatusEnum.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.ai.enums; +package cn.iocoder.yudao.module.ai.enums.image; import lombok.AllArgsConstructor; import lombok.Getter; @@ -13,7 +13,7 @@ import lombok.Getter; public enum AiImageStatusEnum { IN_PROGRESS("10", "进行中"), - COMPLETE("20", "完成"), + SUCCESS("20", "完成"), FAIL("30", "失败"); /** diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiModelEnum.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiModelEnum.java similarity index 98% rename from yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiModelEnum.java rename to yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiModelEnum.java index 3d30d27cb..12844959b 100644 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiModelEnum.java +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiModelEnum.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.ai.enums; +package cn.iocoder.yudao.module.ai.enums.model; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/resources/http/image.http b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.http similarity index 52% rename from yudao-module-ai/yudao-module-ai-biz/src/main/resources/http/image.http rename to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.http index bf1165773..4af24f792 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/resources/http/image.http +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.http @@ -1,17 +1,33 @@ -### chat dallDrawing - -POST {{baseUrl}}/admin-api/ai/image/dallDrawing +### 生成图片:OpenAI(DALL) +POST {{baseUrl}}/ai/image/draw Content-Type: application/json Authorization: {{token}} { - "modal": "dall-e-3", - "size": "1024x1024", - "style": "vivid", - "prompt": "中国长城" + "platform": "OpenAI", + "prompt": "可爱的小喵星人", + "model": "dall-e-3", + "height": "1024", + "width": "1024", + "options": { + "style": "vivid" + } } +### 生成图片:StableDiffusion +POST {{baseUrl}}/ai/image/draw +Content-Type: application/json +Authorization: {{token}} + +{ + "platform": "StableDiffusion", + "prompt": "中国长城", + "model": "stable-diffusion-v1-6", + "height": "1024", + "width": "1024", + "style": "vivid" +} ### chat midjourney 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 15e4304e6..0f2116a0f 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 @@ -6,7 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.ai.client.vo.MidjourneyNotifyReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO; import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO; import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageRespVO; import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; @@ -48,13 +48,10 @@ public class AiImageController { return success(BeanUtils.toBean(image, AiImageRespVO.class)); } - // TODO @fan:建议把 dallDrawing、midjourney 融合成一个 draw 接口,异步绘制;然后返回一个 id 给前端;前端通过 get 接口轮询,直到获取到生成成功 - // TODO @芋艿: 参数差异较大 - // TODO @fan:直接参数平铺?写好注释,要么? - @Operation(summary = "dall2/dall3绘画", description = "openAi dall3是付费的!") - @PostMapping("/dall") - public CommonResult dall(@Validated @RequestBody AiImageDallReqVO req) { - return success(imageService.dall(getLoginUserId(), req)); + @Operation(summary = "生成图片") + @PostMapping("/draw") + public CommonResult drawImage(@Validated @RequestBody AiImageDrawReqVO drawReqVO) { + return success(imageService.drawImage(getLoginUserId(), drawReqVO)); } @Operation(summary = "删除【我的】绘画记录") @@ -73,11 +70,11 @@ public class AiImageController { return success(imageService.midjourneyImagine(getLoginUserId(), req)); } - // TODO @fan:可以考虑,复用 AiImageDallRespVO,统一成 AIImageRespVO + // TODO @芋艿:不拦截 @Operation(summary = "midjourney proxy - 回调通知") @RequestMapping("/midjourney-notify") public CommonResult midjourneyNotify(MidjourneyNotifyReqVO notifyReqVO) { return success(imageService.midjourneyNotify(getLoginUserId(), notifyReqVO)); } -} +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageDallReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageDallReqVO.java deleted file mode 100644 index 1bfe48943..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageDallReqVO.java +++ /dev/null @@ -1,41 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.image.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; -import lombok.Data; - -/** - * dall2/dall2 绘画 - * - * @author fansili - * @time 2024/4/25 16:24 - * @since 1.0 - */ -@Data -public class AiImageDallReqVO { - - @Schema(description = "模型平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private String platform; // 参见 AiPlatformEnum 枚举 - - @Schema(description = "提示词") - @NotNull(message = "提示词不能为空!") - @Size(max = 1200, message = "提示词最大1200") - private String prompt; - - @Schema(description = "模型(dall2、dall3)") - @NotNull(message = "模型不能为空") - private String model; - - @Schema(description = "图像生成的风格。可为vivid(生动)或natural(自然)") - private String style; - - @Schema(description = "图片高度。对于dall-e-2模型,尺寸可为256x256, 512x512, 或 1024x1024。对于dall-e-3模型,尺寸可为1024x1024, 1792x1024, 或 1024x1792。") - @NotNull(message = "图片高度不能为空!") - private Integer height; - - @Schema(description = "图片宽度。对于dall-e-2模型,尺寸可为256x256, 512x512, 或 1024x1024。对于dall-e-3模型,尺寸可为1024x1024, 1792x1024, 或 1024x1792。") - @NotNull(message = "图片宽度不能为空!") - private Integer width; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageDrawReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageDrawReqVO.java new file mode 100644 index 000000000..1fd1323bf --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageDrawReqVO.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.ai.controller.admin.image.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; +import org.springframework.ai.openai.OpenAiImageOptions; +import org.springframework.ai.stabilityai.api.StabilityAiImageOptions; + +import java.util.Map; + +@Schema(description = "管理后台 - 绘画 Request VO") +@Data +public class AiImageDrawReqVO { + + @Schema(description = "模型平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI") + private String platform; // 参见 AiPlatformEnum 枚举 + + @Schema(description = "提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "画一个长城") + @NotEmpty(message = "提示词不能为空") + @Size(max = 1200, message = "提示词最大 1200") + private String prompt; + + @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "stable-diffusion-v1-6") + @NotEmpty(message = "模型不能为空") + private String model; + + /** + * 1. dall-e-2 模型:256x256、512x512、1024x1024 + * 2. dall-e-3 模型:1024x1024, 1792x1024, 或 1024x1792 + */ + @Schema(description = "图片高度") + @NotNull(message = "图片高度不能为空") + private Integer height; + + @Schema(description = "图片宽度") + @NotNull(message = "图片宽度不能为空") + private Integer width; + + // ========== 各平台绘画的拓展参数 ========== + + /** + * 绘制参数,不同 platform 的不同参数 + * + * 1. {@link OpenAiImageOptions} + * 2. {@link StabilityAiImageOptions} + */ + @Schema(description = "绘制参数") + private Map options; + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageMidjourneyOperateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageMidjourneyOperateReqVO.java deleted file mode 100644 index 994c1c5f0..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageMidjourneyOperateReqVO.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.image.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Data; -import lombok.experimental.Accessors; - -/** - * midjourney req - * - * @author fansili - * @time 2024/4/28 17:42 - * @since 1.0 - */ -@Data -@Accessors(chain = true) -public class AiImageMidjourneyOperateReqVO { - - - @NotNull(message = "图片编号不能为空") - @Schema(description = "编号") - private Long id; - - @NotNull(message = "消息编号不能为空") - @Schema(description = "消息编号") - private String messageId; - - @NotNull(message = "操作编号不能为空") - @Schema(description = "操作编号") - private String operateId; -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageRespVO.java index 4d12f7f4b..92578ce0c 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageRespVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageRespVO.java @@ -5,63 +5,44 @@ import lombok.Data; import java.util.Map; -// TODO @芋艿:完善 swagger 注解 +@Schema(description = "管理后台 - 绘画 Response VO") @Data public class AiImageRespVO { - @Schema(description = "id编号", example = "1") + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Long id; - @Schema(description = "用户id", example = "1") + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Long userId; - @Schema(description = "提示词", example = "南极的小企鹅") - private String prompt; + @Schema(description = "平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI") + private String platform; // 参见 AiPlatformEnum 枚举 - @Schema(description = "平台", example = "openai") - private String platform; - - @Schema(description = "模型", example = "dall2") + @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "stable-diffusion-v1-6") private String model; - @Schema(description = "图片宽度", example = "1024") - private String width; + @Schema(description = "提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "南极的小企鹅") + private String prompt; - @Schema(description = "图片高度", example = "1024") - private String height; + @Schema(description = "图片宽度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Integer width; - @Schema(description = "绘画状态:10 进行中、20 绘画完成、30 绘画失败", example = "10") - private String status; + @Schema(description = "图片高度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Integer height; - @Schema(description = "是否发布", example = "public") - private String publicStatus; + @Schema(description = "绘画状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer status; - @Schema(description = "图片地址(自己服务器)", example = "http://") + @Schema(description = "是否发布", requiredMode = Schema.RequiredMode.REQUIRED, example = "public") + private Boolean publicStatus; + + @Schema(description = "图片地址", example = "https://www.iocoder.cn/1.png") private String picUrl; - @Schema(description = "绘画图片地址(绘画好的服务器)", example = "http://") - private String originalPicUrl; - @Schema(description = "绘画错误信息", example = "图片错误信息") private String errorMessage; - // ============ 绘画请求参数 - - // todo @fan:下面的 style、mjNonceId 直接就不用注释啦,直接去看 DO 完事哈 - - /** - * - style - */ - @Schema(description = "绘画请求参数") - private Map drawRequest; - - /** - * - mjNonceId - * - mjOperationId - * - mjOperationName - * - mjOperations - */ - @Schema(description = "绘画请求响应参数") - private Map drawResponse; + @Schema(description = "绘制参数") + private Map options; } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java index 8e582f621..67cd490e5 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java @@ -3,14 +3,13 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.chat; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; -import cn.iocoder.yudao.module.ai.enums.AiModelEnum; +import cn.iocoder.yudao.module.ai.enums.model.AiModelEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; import java.time.LocalDateTime; -import java.util.Date; /** * AI Chat 对话 DO diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java index 455a428a7..61608b8cc 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java @@ -5,7 +5,7 @@ import org.springframework.ai.chat.messages.MessageType; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; -import cn.iocoder.yudao.module.ai.enums.AiModelEnum; +import cn.iocoder.yudao.module.ai.enums.model.AiModelEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java index b21b2f8d8..b4fc6676a 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java @@ -1,13 +1,17 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.image; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; +import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import org.springframework.ai.openai.OpenAiImageOptions; +import org.springframework.ai.stabilityai.api.StabilityAiImageOptions; import java.util.Map; @@ -16,70 +20,86 @@ import java.util.Map; * * @author fansili */ -@TableName("ai_image") +@TableName(value = "ai_image", autoResultMap = true) @Data public class AiImageDO extends BaseDO { // TODO @fan:1)使用 java 注释哈,不要注解。2)关联、枚举字段,要关联到对应类,参考 AiChatMessageDO 的注释 + /** + * 编号 + */ @TableId(type = IdType.AUTO) private Long id; - @Schema(description = "用户编号") + /** + * 用户编号 + * + * 关联 {@link AdminUserRespDTO#getId()} + */ private Long userId; - @Schema(description = "midjourney proxy 关联的 job id") - private String jobId; - - @Schema(description = "提示词") + /** + * 提示词 + */ private String prompt; - @Schema(description = "平台") + /** + * 平台 + * + * 枚举 {@link cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum} + */ private String platform; - - @Schema(description = "模型") + /** + * 模型 + * + * 冗余 {@link AiChatModelDO#getModel()} + */ private String model; - @Schema(description = "图片宽度") + /** + * 图片宽度 + */ private Integer width; - - @Schema(description = "图片高度") + /** + * 图片高度 + */ private Integer height; // TODO @fan:这种就注释绘画状态,然后枚举类关联下就好啦 - @Schema(description = "绘画状态:提交、排队、绘画中、绘画完成、绘画失败") + /** + * 生成状态 + * + * 枚举 {@link AiImageStatusEnum} + */ private String status; - @Schema(description = "是否发布") - private String publicStatus; - - @Schema(description = "图片地址(自己服务器)") + /** + * 图片地址 + */ private String picUrl; - - // TODO @芋艿:可能要删除掉 - @Schema(description = "绘画图片地址(绘画好的服务器)") - private String originalPicUrl; - - // ============ 绘画请求参数 ============ + /** + * 是否公开 + */ + private Boolean publicStatus; /** - * - style + * 绘制参数,不同 platform 的不同参数 + * + * 1. {@link OpenAiImageOptions} + * 2. {@link StabilityAiImageOptions} */ - @Schema(description = "绘画请求参数") @TableField(typeHandler = JacksonTypeHandler.class) - private Map drawRequest; + private Map options; + // TODO @芋艿:再瞅瞅 /** - * - mjNonceId - * - mjOperationId - * - mjOperationName - * - mjOperations + * midjourney proxy 关联的 job id + */ + private String jobId; + /** + * 绘画错误信息 */ - @Schema(description = "绘画请求响应参数") - @TableField(typeHandler = JacksonTypeHandler.class) - private Map drawResponse; - - @Schema(description = "绘画错误信息") private String errorMessage; } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java index 85349b760..83dcd8dff 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java @@ -25,8 +25,8 @@ import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.CHAT_CONVERSATION_MODEL_ERROR; -import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS; +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_MODEL_ERROR; +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS; /** * AI 聊天对话 Service 实现类 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 93d8167ca..27dd154ae 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 @@ -8,7 +8,7 @@ import cn.iocoder.yudao.framework.ai.core.factory.AiClientFactory; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.ai.ErrorCodeConstants; +import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO; @@ -39,8 +39,8 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU 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; +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.AI_CHAT_MESSAGE_NOT_EXIST; +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS; /** * AI 聊天消息 Service 实现类 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 index 2442e79a9..381a6ba17 100644 --- 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 @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.ai.service.image; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.ai.client.vo.MidjourneyNotifyReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO; import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; @@ -32,12 +32,12 @@ public interface AiImageService { AiImageDO getImage(Long id); /** - * ai绘画 - dall2/dall3 绘画 + * 绘制图片 * - * @param loginUserId - * @param req + * @param userId 用户编号 + * @param drawReqVO 绘制请求 */ - Long dall(Long loginUserId, AiImageDallReqVO req); + Long drawImage(Long userId, AiImageDrawReqVO drawReqVO); /** * midjourney 图片生成 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 index a9a084d65..b0c675030 100644 --- 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 @@ -1,19 +1,16 @@ package cn.iocoder.yudao.module.ai.service.image; -import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.codec.Base64; +import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; import cn.hutool.http.HttpUtil; import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; -import cn.iocoder.yudao.framework.ai.core.enums.OpenAiImageModelEnum; -import cn.iocoder.yudao.framework.ai.core.enums.OpenAiImageStyleEnum; import cn.iocoder.yudao.framework.ai.core.factory.AiClientFactory; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.ai.AiCommonConstants; import cn.iocoder.yudao.module.ai.client.MidjourneyProxyClient; import cn.iocoder.yudao.module.ai.client.enums.MidjourneyModelEnum; import cn.iocoder.yudao.module.ai.client.enums.MidjourneySubmitCodeEnum; @@ -21,17 +18,18 @@ import cn.iocoder.yudao.module.ai.client.enums.MidjourneyTaskStatusEnum; import cn.iocoder.yudao.module.ai.client.vo.MidjourneyImagineReqVO; import cn.iocoder.yudao.module.ai.client.vo.MidjourneyNotifyReqVO; import cn.iocoder.yudao.module.ai.client.vo.MidjourneySubmitRespVO; -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO; import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO; 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.AiImagePublicStatusEnum; -import cn.iocoder.yudao.module.ai.enums.AiImageStatusEnum; +import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum; import cn.iocoder.yudao.module.infra.api.file.FileApi; -import com.google.common.collect.ImmutableMap; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; -import org.springframework.ai.image.*; +import org.springframework.ai.image.ImageClient; +import org.springframework.ai.image.ImageOptions; +import org.springframework.ai.image.ImagePrompt; +import org.springframework.ai.image.ImageResponse; import org.springframework.ai.openai.OpenAiImageOptions; import org.springframework.ai.stabilityai.api.StabilityAiImageOptions; import org.springframework.beans.factory.annotation.Autowired; @@ -41,7 +39,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.AI_IMAGE_NOT_EXISTS; +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.AI_IMAGE_NOT_EXISTS; /** * AI 绘画 Service 实现类 @@ -78,22 +76,18 @@ public class AiImageServiceImpl implements AiImageService { } @Override - public Long dall(Long userId, AiImageDallReqVO req) { - req.setPlatform("dall"); // TODO 芋艿:临时写死 + public Long drawImage(Long userId, AiImageDrawReqVO drawReqVO) { // 1. 保存数据库 - AiImageDO image = BeanUtils.toBean(req, AiImageDO.class) - .setUserId(userId).setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()) - .setWidth(req.getWidth()).setHeight(req.getHeight()) - .setDrawRequest(ImmutableMap.of(AiCommonConstants.DRAW_REQ_KEY_STYLE, req.getStyle())) - .setPublicStatus(AiImagePublicStatusEnum.PRIVATE.getStatus()); + AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false) + .setWidth(drawReqVO.getWidth()).setHeight(drawReqVO.getHeight()).setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()); imageMapper.insert(image); - // 2. 异步绘制,后续前端通过返回的 id 进行伦旭 - getSelf().doDall(image, req); + // 2. 异步绘制,后续前端通过返回的 id 进行轮询结果 + getSelf().doDall(image, drawReqVO); return image.getId(); } @Async - public void doDall(AiImageDO image, AiImageDallReqVO req) { + public void doDall(AiImageDO image, AiImageDrawReqVO req) { try { // 1.1 构建请求 ImageOptions request = buildImageOptions(req); @@ -106,7 +100,7 @@ public class AiImageServiceImpl implements AiImageService { String filePath = fileApi.createFile(fileContent); // 3. 更新数据库 - imageMapper.updateById(new AiImageDO().setId(image.getId()).setStatus(AiImageStatusEnum.COMPLETE.getStatus()) + imageMapper.updateById(new AiImageDO().setId(image.getId()).setStatus(AiImageStatusEnum.SUCCESS.getStatus()) .setPicUrl(filePath)); } catch (Exception ex) { log.error("[doDall][image({}) 生成异常]", image, ex); @@ -115,30 +109,28 @@ public class AiImageServiceImpl implements AiImageService { } } - private static ImageOptions buildImageOptions(AiImageDallReqVO draw) { - if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.OPEN_AI_DALL.getPlatform())) { - OpenAiImageOptions request = new OpenAiImageOptions(); - request.setModel(OpenAiImageModelEnum.valueOfModel(draw.getModel()).getModel()); - request.setStyle(OpenAiImageStyleEnum.valueOfStyle(draw.getStyle()).getStyle()); - request.setSize(String.format(AiCommonConstants.DALL_SIZE_TEMPLATE, draw.getWidth(), draw.getHeight())); - request.setResponseFormat("b64_json"); - return request; - } else { - // https://platform.stability.ai/docs/api-reference#tag/Generate/paths/~1v2beta~1stable-image~1generate~1sd3/post - return StabilityAiImageOptions.builder().withModel(draw.getModel()) + private static ImageOptions buildImageOptions(AiImageDrawReqVO draw) { + if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.OPENAI.getPlatform())) { + // https://platform.openai.com/docs/api-reference/images/create + return OpenAiImageOptions.builder().withModel(draw.getModel()) .withHeight(draw.getHeight()).withWidth(draw.getWidth()) + .withStyle(MapUtil.getStr(draw.getOptions(), "style")) // 风格 + .withResponseFormat("b64_json") + .build(); + } else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.STABLE_DIFFUSION.getPlatform())) { + // https://platform.stability.ai/docs/api-reference#tag/Text-to-Image/operation/textToImage + return StabilityAiImageOptions.builder().withModel(draw.getModel()) + .withHeight(draw.getHeight()).withWidth(draw.getWidth()) // TODO @芋艿:各种参数 .build(); } -// return null; + throw new IllegalArgumentException("不支持的 AI 平台:" + draw.getPlatform()); } @Override @Transactional(rollbackFor = Exception.class) public Long midjourneyImagine(Long loginUserId, AiImageMidjourneyImagineReqVO req) { - // 1、构建 AiImageDO AiImageDO aiImageDO = new AiImageDO(); - aiImageDO.setId(null); aiImageDO.setUserId(loginUserId); aiImageDO.setPrompt(req.getPrompt()); aiImageDO.setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform()); @@ -147,12 +139,6 @@ public class AiImageServiceImpl implements AiImageService { aiImageDO.setWidth(null); aiImageDO.setHeight(null); aiImageDO.setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()); - aiImageDO.setPublicStatus(AiImagePublicStatusEnum.PRIVATE.getStatus()); - aiImageDO.setPicUrl(null); - aiImageDO.setOriginalPicUrl(null); - aiImageDO.setDrawRequest(null); - aiImageDO.setDrawResponse(null); - aiImageDO.setErrorMessage(null); // 2、保存 image imageMapper.insert(aiImageDO); @@ -211,7 +197,7 @@ public class AiImageServiceImpl implements AiImageService { // String imageStatus = null; if (MidjourneyTaskStatusEnum.SUCCESS == notifyReqVO.getStatus()) { - imageStatus = AiImageStatusEnum.COMPLETE.getStatus(); + imageStatus = AiImageStatusEnum.SUCCESS.getStatus(); } else if (MidjourneyTaskStatusEnum.FAILURE == notifyReqVO.getStatus()) { imageStatus = AiImageStatusEnum.FAIL.getStatus(); } @@ -226,8 +212,7 @@ public class AiImageServiceImpl implements AiImageService { .setId(image.getId()) .setStatus(imageStatus) .setPicUrl(filePath) - .setOriginalPicUrl(notifyReqVO.getImageUrl()) - .setDrawResponse(BeanUtil.beanToMap(notifyReqVO)) +// .setOriginalPicUrl(notifyReqVO.getImageUrl()) TODO @fan:就不存原始的图片地址啦 ); return true; } 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 index 0568bbec6..493d0cded 100644 --- 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 @@ -5,7 +5,7 @@ import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyOperationsVO; 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.AiImageStatusEnum; +import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum; import com.alibaba.fastjson.JSON; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -89,7 +89,7 @@ public class YuDaoMidjourneyMessageHandler implements MidjourneyMessageHandler { AiImageStatusEnum drawingStatusEnum = null; String generateStatus = midjourneyMessage.getGenerateStatus(); if (MidjourneyGennerateStatusEnum.COMPLETED.getStatus().equals(generateStatus)) { - drawingStatusEnum = AiImageStatusEnum.COMPLETE; + drawingStatusEnum = AiImageStatusEnum.SUCCESS; } else if (MidjourneyGennerateStatusEnum.IN_PROGRESS.getStatus().equals(generateStatus)) { drawingStatusEnum = AiImageStatusEnum.IN_PROGRESS; } @@ -101,7 +101,6 @@ public class YuDaoMidjourneyMessageHandler implements MidjourneyMessageHandler { // 更新数据库 aiImageMapper.updateByMjNonce(nonceId, new AiImageDO() - .setOriginalPicUrl(imageUrl) .setStatus(drawingStatusEnum == null ? null : drawingStatusEnum.getStatus()) // .setMjNonceId(midjourneyMessage.getId()) // .setMjOperations(JsonUtils.toJsonString(midjourneyOperations)) 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 deleted file mode 100644 index c3d2b77cc..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalChatConfigVO.java +++ /dev/null @@ -1,35 +0,0 @@ -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 deleted file mode 100644 index 426641a0d..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalConfigVO.java +++ /dev/null @@ -1,31 +0,0 @@ -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 deleted file mode 100644 index f07b44647..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalDallConfigVO.java +++ /dev/null @@ -1,41 +0,0 @@ -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 deleted file mode 100644 index 6c91ca652..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalMidjourneyConfigVO.java +++ /dev/null @@ -1,16 +0,0 @@ -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/model/AiApiKeyServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java index e4db8125b..41cce28f8 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java @@ -17,7 +17,7 @@ import org.springframework.validation.annotation.Validated; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; /** * AI API 密钥 Service 实现类 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 31d048c70..4b11602f5 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 @@ -16,7 +16,7 @@ import java.util.Collection; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; /** * AI 聊天模型 Service 实现类 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 faee09d06..4f358e473 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 @@ -21,7 +21,7 @@ import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; /** * AI 聊天角色 Service 实现类 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 e0cfa05f9..e56a23996 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,10 +3,9 @@ package cn.iocoder.yudao.framework.ai.config; import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel; import cn.iocoder.yudao.framework.ai.core.model.yiyan.api.YiYanChatModel; -import cn.iocoder.yudao.framework.ai.core.enums.OpenAiImageModelEnum; -import cn.iocoder.yudao.framework.ai.core.enums.OpenAiImageStyleEnum; import lombok.Data; import lombok.experimental.Accessors; +import org.springframework.ai.autoconfigure.openai.OpenAiImageProperties; import org.springframework.boot.context.properties.ConfigurationProperties; /** @@ -92,27 +91,6 @@ public class YudaoAiProperties { private int refreshTokenSecondTime = 86400; } - @Data - @Accessors(chain = true) - public static class OpenAiImageProperties { - - private boolean enable = false; - - /** - * api key - */ - private String apiKey; - /** - * 模型 - */ - private OpenAiImageModelEnum model = OpenAiImageModelEnum.DALL_E_2; - /** - * 风格 - */ - private OpenAiImageStyleEnum style = OpenAiImageStyleEnum.VIVID; - - } - @Data @Accessors(chain = true) public static class MidjourneyProperties { diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/AiPlatformEnum.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/AiPlatformEnum.java index d1fabd5f4..d6513354e 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/AiPlatformEnum.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/AiPlatformEnum.java @@ -20,7 +20,6 @@ public enum AiPlatformEnum { QIAN_WEN("QianWen", "千问"), // 阿里 GEMIR ("gemir ", "gemir "), // 谷歌 - OPEN_AI_DALL("dall", "dall"), // TODO OpenAI 提供的绘图,接入中;TODO 要不要统一下?! STABLE_DIFFUSION("StableDiffusion", "StableDiffusion"), // Stability AI MIDJOURNEY("midjourney", "midjourney"), // TODO MJ 提供的绘图,接入中 ; diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/OpenAiImageModelEnum.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/OpenAiImageModelEnum.java deleted file mode 100644 index 203f435cc..000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/OpenAiImageModelEnum.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.framework.ai.core.enums; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -// TODO 芋艿:待梳理 -/** - * open ai - * - * @author fansili - * @time 2024/4/28 14:21 - * @since 1.0 - */ -@AllArgsConstructor -@Getter -@Deprecated -public enum OpenAiImageModelEnum { - - DALL_E_2("dall-e-2", "dall-e-2"), - - DALL_E_3("dall-e-3", "dall-e-3") - - ; - - private String model; - - private String name; - - public static OpenAiImageModelEnum valueOfModel(String model) { - for (OpenAiImageModelEnum itemEnum : OpenAiImageModelEnum.values()) { - if (itemEnum.getModel().equals(model)) { - return itemEnum; - } - } - throw new IllegalArgumentException("Invalid MessageType value: " + model); - } -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/OpenAiImageStyleEnum.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/OpenAiImageStyleEnum.java deleted file mode 100644 index 447621391..000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/OpenAiImageStyleEnum.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.iocoder.yudao.framework.ai.core.enums; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -// TODO 芋艿:待梳理 -/** - * open ai image style - * - * @author fansili - * @time 2024/4/28 16:15 - * @since 1.0 - */ -@AllArgsConstructor -@Getter -@Deprecated -public enum OpenAiImageStyleEnum { - - // 图像生成的风格。可为vivid(生动)或 natural(自然)。vivid会使模型偏向生成超现实和戏剧性的图像,而natural则会让模型产出更自然、不那么超现实的图像。该参数仅对dall-e-3模型有效。 - - VIVID("vivid", "生动"), - NATURAL("natural", "自然"), - - ; - - private String style; - - private String name; - - public static OpenAiImageStyleEnum valueOfStyle(String style) { - for (OpenAiImageStyleEnum itemEnum : OpenAiImageStyleEnum.values()) { - if (itemEnum.getStyle().equals(style)) { - return itemEnum; - } - } - throw new IllegalArgumentException("Invalid MessageType value: " + style); - } -} 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 fa70af490..5c8248789 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 @@ -33,6 +33,7 @@ import org.springframework.ai.openai.OpenAiChatOptions; import org.springframework.ai.openai.OpenAiImageClient; import org.springframework.ai.openai.api.ApiUtils; import org.springframework.ai.openai.api.OpenAiApi; +import org.springframework.ai.stabilityai.StabilityAiImageClient; import java.util.List; @@ -88,12 +89,15 @@ public class AiClientFactoryImpl implements AiClientFactory { @Override public ImageClient getDefaultImageClient(AiPlatformEnum platform) { + //noinspection EnhancedSwitchMigration switch (platform) { - case OPEN_AI_DALL: + case OPENAI: return SpringUtil.getBean(OpenAiImageClient.class); - + case STABLE_DIFFUSION: + return SpringUtil.getBean(StabilityAiImageClient.class); + default: + throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); } - return null; } private static String buildClientCacheKey(Class clazz, Object... params) {