Commit 6856f5f192d4c726a67ef3b45c9e744a49c735ae

Authored by YunaiV
1 parent 05635031

【新增】AI:进一步统一 DALL、SD 的绘制实现

Showing 34 changed files with 220 additions and 587 deletions
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/AiCommonConstants.java deleted
1 -package cn.iocoder.yudao.module.ai;  
2 -  
3 -/**  
4 - * ai 常用的常量  
5 - *  
6 - * @author fansili  
7 - * @time 2024/5/7 09:29  
8 - * @since 1.0  
9 - */  
10 -public class AiCommonConstants {  
11 -  
12 - /**  
13 - * 绘画 request - style  
14 - */  
15 - public static final String DRAW_REQ_KEY_STYLE = "style";  
16 - /**  
17 - * dall size - 模板(1024x1024)  
18 - */  
19 - public static final String DALL_SIZE_TEMPLATE = "%sx%s";  
20 -  
21 -}  
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/api/package-info.java 0 → 100644
  1 +/**
  2 + * 占位,没有特别的作用
  3 + */
  4 +package cn.iocoder.yudao.module.ai.api;
0 \ No newline at end of file 5 \ No newline at end of file
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatConversationTypeEnum.java deleted
1 -package cn.iocoder.yudao.module.ai.enums;  
2 -  
3 -import lombok.AllArgsConstructor;  
4 -import lombok.Getter;  
5 -  
6 -/**  
7 - * 对话类型  
8 - * 创建对话、继续对话  
9 - *  
10 - * @author fansili  
11 - * @time 2024/4/14 18:15  
12 - * @since 1.0  
13 - */  
14 -@AllArgsConstructor  
15 -@Getter  
16 -public enum AiChatConversationTypeEnum {  
17 -  
18 - // roleChat、userChat  
19 - ROLE_CHAT("roleChat", "角色对话"),  
20 - USER_CHAT("userChat", "用户对话"),  
21 -  
22 - ;  
23 -  
24 - private String type;  
25 -  
26 - private String name;  
27 -  
28 - public static AiChatConversationTypeEnum valueOfType(String type) {  
29 - for (AiChatConversationTypeEnum itemEnum : AiChatConversationTypeEnum.values()) {  
30 - if (itemEnum.getType().equals(type)) {  
31 - return itemEnum;  
32 - }  
33 - }  
34 - throw new IllegalArgumentException("Invalid MessageType value: " + type);  
35 - }  
36 -  
37 -}  
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleCategoryEnum.java deleted
1 -package cn.iocoder.yudao.module.ai.enums;  
2 -  
3 -import lombok.AllArgsConstructor;  
4 -import lombok.Getter;  
5 -  
6 -/**  
7 - * 聊天role 分类  
8 - *  
9 - * @author fansili  
10 - * @time 2024/4/24 16:41  
11 - * @since 1.0  
12 - */  
13 -@AllArgsConstructor  
14 -@Getter  
15 -public enum AiChatRoleCategoryEnum {  
16 -  
17 - WRITING("writing", "写作"),  
18 -  
19 - ENTERTAINMENT("entertainment", "娱乐"),  
20 -  
21 - ;  
22 -  
23 -  
24 - private String category;  
25 -  
26 - private String name;  
27 -  
28 -  
29 - public static AiChatRoleCategoryEnum valueOfCategory(String category) {  
30 - for (AiChatRoleCategoryEnum itemEnum : AiChatRoleCategoryEnum.values()) {  
31 - if (itemEnum.getCategory().equals(category)) {  
32 - return itemEnum;  
33 - }  
34 - }  
35 - throw new IllegalArgumentException("Invalid MessageType value: " + category);  
36 - }  
37 -}  
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiImagePublicStatusEnum.java deleted
1 -package cn.iocoder.yudao.module.ai.enums;  
2 -  
3 -import lombok.AllArgsConstructor;  
4 -import lombok.Getter;  
5 -  
6 -/**  
7 - * ai绘画 public 状态  
8 - *  
9 - * @author fansili  
10 - * @time 2024/4/28 17:05  
11 - * @since 1.0  
12 - */  
13 -@AllArgsConstructor  
14 -@Getter  
15 -public enum AiImagePublicStatusEnum {  
16 -  
17 - PRIVATE("private", "私有"),  
18 - PUBLIC("public", "公开"),  
19 -  
20 - ;  
21 -  
22 - // TODO @fan:final 一下  
23 - private final String status;  
24 -  
25 - private final String name;  
26 -  
27 -  
28 - public static AiImagePublicStatusEnum valueOfStatus(String status) {  
29 - for (AiImagePublicStatusEnum itemEnum : AiImagePublicStatusEnum.values()) {  
30 - if (itemEnum.getStatus().equals(status)) {  
31 - return itemEnum;  
32 - }  
33 - }  
34 - throw new IllegalArgumentException("Invalid MessageType value: " + status);  
35 - }  
36 -}  
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java renamed to yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java
1 -package cn.iocoder.yudao.module.ai; 1 +package cn.iocoder.yudao.module.ai.enums;
2 2
3 import cn.iocoder.yudao.framework.common.exception.ErrorCode; 3 import cn.iocoder.yudao.framework.common.exception.ErrorCode;
4 4
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiImageStatusEnum.java renamed to yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/image/AiImageStatusEnum.java
1 -package cn.iocoder.yudao.module.ai.enums; 1 +package cn.iocoder.yudao.module.ai.enums.image;
2 2
3 import lombok.AllArgsConstructor; 3 import lombok.AllArgsConstructor;
4 import lombok.Getter; 4 import lombok.Getter;
@@ -13,7 +13,7 @@ import lombok.Getter; @@ -13,7 +13,7 @@ import lombok.Getter;
13 public enum AiImageStatusEnum { 13 public enum AiImageStatusEnum {
14 14
15 IN_PROGRESS("10", "进行中"), 15 IN_PROGRESS("10", "进行中"),
16 - COMPLETE("20", "完成"), 16 + SUCCESS("20", "完成"),
17 FAIL("30", "失败"); 17 FAIL("30", "失败");
18 18
19 /** 19 /**
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiModelEnum.java renamed to yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiModelEnum.java
1 -package cn.iocoder.yudao.module.ai.enums; 1 +package cn.iocoder.yudao.module.ai.enums.model;
2 2
3 import lombok.AllArgsConstructor; 3 import lombok.AllArgsConstructor;
4 import lombok.Getter; 4 import lombok.Getter;
yudao-module-ai/yudao-module-ai-biz/src/main/resources/http/image.http renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.http
1 1
2 -### chat dallDrawing  
3 -  
4 -POST {{baseUrl}}/admin-api/ai/image/dallDrawing 2 +### 生成图片:OpenAI(DALL)
  3 +POST {{baseUrl}}/ai/image/draw
5 Content-Type: application/json 4 Content-Type: application/json
6 Authorization: {{token}} 5 Authorization: {{token}}
7 6
8 { 7 {
9 - "modal": "dall-e-3",  
10 - "size": "1024x1024",  
11 - "style": "vivid",  
12 - "prompt": "中国长城" 8 + "platform": "OpenAI",
  9 + "prompt": "可爱的小喵星人",
  10 + "model": "dall-e-3",
  11 + "height": "1024",
  12 + "width": "1024",
  13 + "options": {
  14 + "style": "vivid"
  15 + }
13 } 16 }
14 17
  18 +### 生成图片:StableDiffusion
  19 +POST {{baseUrl}}/ai/image/draw
  20 +Content-Type: application/json
  21 +Authorization: {{token}}
  22 +
  23 +{
  24 + "platform": "StableDiffusion",
  25 + "prompt": "中国长城",
  26 + "model": "stable-diffusion-v1-6",
  27 + "height": "1024",
  28 + "width": "1024",
  29 + "style": "vivid"
  30 +}
15 31
16 ### chat midjourney 32 ### chat midjourney
17 33
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; @@ -6,7 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
8 import cn.iocoder.yudao.module.ai.client.vo.MidjourneyNotifyReqVO; 8 import cn.iocoder.yudao.module.ai.client.vo.MidjourneyNotifyReqVO;
9 -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallReqVO; 9 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO;
10 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO; 10 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO;
11 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageRespVO; 11 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageRespVO;
12 import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; 12 import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
@@ -48,13 +48,10 @@ public class AiImageController { @@ -48,13 +48,10 @@ public class AiImageController {
48 return success(BeanUtils.toBean(image, AiImageRespVO.class)); 48 return success(BeanUtils.toBean(image, AiImageRespVO.class));
49 } 49 }
50 50
51 - // TODO @fan:建议把 dallDrawing、midjourney 融合成一个 draw 接口,异步绘制;然后返回一个 id 给前端;前端通过 get 接口轮询,直到获取到生成成功  
52 - // TODO @芋艿: 参数差异较大  
53 - // TODO @fan:直接参数平铺?写好注释,要么?  
54 - @Operation(summary = "dall2/dall3绘画", description = "openAi dall3是付费的!")  
55 - @PostMapping("/dall")  
56 - public CommonResult<Long> dall(@Validated @RequestBody AiImageDallReqVO req) {  
57 - return success(imageService.dall(getLoginUserId(), req)); 51 + @Operation(summary = "生成图片")
  52 + @PostMapping("/draw")
  53 + public CommonResult<Long> drawImage(@Validated @RequestBody AiImageDrawReqVO drawReqVO) {
  54 + return success(imageService.drawImage(getLoginUserId(), drawReqVO));
58 } 55 }
59 56
60 @Operation(summary = "删除【我的】绘画记录") 57 @Operation(summary = "删除【我的】绘画记录")
@@ -73,11 +70,11 @@ public class AiImageController { @@ -73,11 +70,11 @@ public class AiImageController {
73 return success(imageService.midjourneyImagine(getLoginUserId(), req)); 70 return success(imageService.midjourneyImagine(getLoginUserId(), req));
74 } 71 }
75 72
76 - // TODO @fan:可以考虑,复用 AiImageDallRespVO,统一成 AIImageRespVO 73 + // TODO @芋艿:不拦截
77 @Operation(summary = "midjourney proxy - 回调通知") 74 @Operation(summary = "midjourney proxy - 回调通知")
78 @RequestMapping("/midjourney-notify") 75 @RequestMapping("/midjourney-notify")
79 public CommonResult<Boolean> midjourneyNotify(MidjourneyNotifyReqVO notifyReqVO) { 76 public CommonResult<Boolean> midjourneyNotify(MidjourneyNotifyReqVO notifyReqVO) {
80 return success(imageService.midjourneyNotify(getLoginUserId(), notifyReqVO)); 77 return success(imageService.midjourneyNotify(getLoginUserId(), notifyReqVO));
81 } 78 }
82 79
83 -} 80 -}
  81 +}
84 \ No newline at end of file 82 \ No newline at end of file
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageDallReqVO.java deleted
1 -package cn.iocoder.yudao.module.ai.controller.admin.image.vo;  
2 -  
3 -import io.swagger.v3.oas.annotations.media.Schema;  
4 -import jakarta.validation.constraints.NotNull;  
5 -import jakarta.validation.constraints.Size;  
6 -import lombok.Data;  
7 -  
8 -/**  
9 - * dall2/dall2 绘画  
10 - *  
11 - * @author fansili  
12 - * @time 2024/4/25 16:24  
13 - * @since 1.0  
14 - */  
15 -@Data  
16 -public class AiImageDallReqVO {  
17 -  
18 - @Schema(description = "模型平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")  
19 - private String platform; // 参见 AiPlatformEnum 枚举  
20 -  
21 - @Schema(description = "提示词")  
22 - @NotNull(message = "提示词不能为空!")  
23 - @Size(max = 1200, message = "提示词最大1200")  
24 - private String prompt;  
25 -  
26 - @Schema(description = "模型(dall2、dall3)")  
27 - @NotNull(message = "模型不能为空")  
28 - private String model;  
29 -  
30 - @Schema(description = "图像生成的风格。可为vivid(生动)或natural(自然)")  
31 - private String style;  
32 -  
33 - @Schema(description = "图片高度。对于dall-e-2模型,尺寸可为256x256, 512x512, 或 1024x1024。对于dall-e-3模型,尺寸可为1024x1024, 1792x1024, 或 1024x1792。")  
34 - @NotNull(message = "图片高度不能为空!")  
35 - private Integer height;  
36 -  
37 - @Schema(description = "图片宽度。对于dall-e-2模型,尺寸可为256x256, 512x512, 或 1024x1024。对于dall-e-3模型,尺寸可为1024x1024, 1792x1024, 或 1024x1792。")  
38 - @NotNull(message = "图片宽度不能为空!")  
39 - private Integer width;  
40 -  
41 -}  
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageDrawReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.image.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotEmpty;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import jakarta.validation.constraints.Size;
  7 +import lombok.Data;
  8 +import org.springframework.ai.openai.OpenAiImageOptions;
  9 +import org.springframework.ai.stabilityai.api.StabilityAiImageOptions;
  10 +
  11 +import java.util.Map;
  12 +
  13 +@Schema(description = "管理后台 - 绘画 Request VO")
  14 +@Data
  15 +public class AiImageDrawReqVO {
  16 +
  17 + @Schema(description = "模型平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
  18 + private String platform; // 参见 AiPlatformEnum 枚举
  19 +
  20 + @Schema(description = "提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "画一个长城")
  21 + @NotEmpty(message = "提示词不能为空")
  22 + @Size(max = 1200, message = "提示词最大 1200")
  23 + private String prompt;
  24 +
  25 + @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "stable-diffusion-v1-6")
  26 + @NotEmpty(message = "模型不能为空")
  27 + private String model;
  28 +
  29 + /**
  30 + * 1. dall-e-2 模型:256x256、512x512、1024x1024
  31 + * 2. dall-e-3 模型:1024x1024, 1792x1024, 或 1024x1792
  32 + */
  33 + @Schema(description = "图片高度")
  34 + @NotNull(message = "图片高度不能为空")
  35 + private Integer height;
  36 +
  37 + @Schema(description = "图片宽度")
  38 + @NotNull(message = "图片宽度不能为空")
  39 + private Integer width;
  40 +
  41 + // ========== 各平台绘画的拓展参数 ==========
  42 +
  43 + /**
  44 + * 绘制参数,不同 platform 的不同参数
  45 + *
  46 + * 1. {@link OpenAiImageOptions}
  47 + * 2. {@link StabilityAiImageOptions}
  48 + */
  49 + @Schema(description = "绘制参数")
  50 + private Map<String, String> options;
  51 +
  52 +}
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageMidjourneyOperateReqVO.java deleted
1 -package cn.iocoder.yudao.module.ai.controller.admin.image.vo;  
2 -  
3 -import io.swagger.v3.oas.annotations.media.Schema;  
4 -import jakarta.validation.constraints.NotNull;  
5 -import lombok.Data;  
6 -import lombok.experimental.Accessors;  
7 -  
8 -/**  
9 - * midjourney req  
10 - *  
11 - * @author fansili  
12 - * @time 2024/4/28 17:42  
13 - * @since 1.0  
14 - */  
15 -@Data  
16 -@Accessors(chain = true)  
17 -public class AiImageMidjourneyOperateReqVO {  
18 -  
19 -  
20 - @NotNull(message = "图片编号不能为空")  
21 - @Schema(description = "编号")  
22 - private Long id;  
23 -  
24 - @NotNull(message = "消息编号不能为空")  
25 - @Schema(description = "消息编号")  
26 - private String messageId;  
27 -  
28 - @NotNull(message = "操作编号不能为空")  
29 - @Schema(description = "操作编号")  
30 - private String operateId;  
31 -}  
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; @@ -5,63 +5,44 @@ import lombok.Data;
5 5
6 import java.util.Map; 6 import java.util.Map;
7 7
8 -// TODO @芋艿:完善 swagger 注解 8 +@Schema(description = "管理后台 - 绘画 Response VO")
9 @Data 9 @Data
10 public class AiImageRespVO { 10 public class AiImageRespVO {
11 11
12 - @Schema(description = "id编号", example = "1") 12 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
13 private Long id; 13 private Long id;
14 14
15 - @Schema(description = "用户id", example = "1") 15 + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
16 private Long userId; 16 private Long userId;
17 17
18 - @Schema(description = "提示词", example = "南极的小企鹅")  
19 - private String prompt;  
20 -  
21 - @Schema(description = "平台", example = "openai")  
22 - private String platform; 18 + @Schema(description = "平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
  19 + private String platform; // 参见 AiPlatformEnum 枚举
23 20
24 - @Schema(description = "模型", example = "dall2") 21 + @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "stable-diffusion-v1-6")
25 private String model; 22 private String model;
26 23
27 - @Schema(description = "图片宽度", example = "1024")  
28 - private String width; 24 + @Schema(description = "提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "南极的小企鹅")
  25 + private String prompt;
29 26
30 - @Schema(description = "图片高度", example = "1024")  
31 - private String height; 27 + @Schema(description = "图片宽度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  28 + private Integer width;
32 29
33 - @Schema(description = "绘画状态:10 进行中、20 绘画完成、30 绘画失败", example = "10")  
34 - private String status; 30 + @Schema(description = "图片高度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  31 + private Integer height;
35 32
36 - @Schema(description = "是否发布", example = "public")  
37 - private String publicStatus; 33 + @Schema(description = "绘画状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
  34 + private Integer status;
38 35
39 - @Schema(description = "图片地址(自己服务器)", example = "http://")  
40 - private String picUrl; 36 + @Schema(description = "是否发布", requiredMode = Schema.RequiredMode.REQUIRED, example = "public")
  37 + private Boolean publicStatus;
41 38
42 - @Schema(description = "绘画图片地址(绘画好的服务器)", example = "http://")  
43 - private String originalPicUrl; 39 + @Schema(description = "图片地址", example = "https://www.iocoder.cn/1.png")
  40 + private String picUrl;
44 41
45 @Schema(description = "绘画错误信息", example = "图片错误信息") 42 @Schema(description = "绘画错误信息", example = "图片错误信息")
46 private String errorMessage; 43 private String errorMessage;
47 44
48 - // ============ 绘画请求参数  
49 -  
50 - // todo @fan:下面的 style、mjNonceId 直接就不用注释啦,直接去看 DO 完事哈  
51 -  
52 - /**  
53 - * - style  
54 - */  
55 - @Schema(description = "绘画请求参数")  
56 - private Map<String, Object> drawRequest;  
57 -  
58 - /**  
59 - * - mjNonceId  
60 - * - mjOperationId  
61 - * - mjOperationName  
62 - * - mjOperations  
63 - */  
64 - @Schema(description = "绘画请求响应参数")  
65 - private Map<String, Object> drawResponse; 45 + @Schema(description = "绘制参数")
  46 + private Map<String, String> options;
66 47
67 } 48 }
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; @@ -3,14 +3,13 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.chat;
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; 4 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
5 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; 5 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
6 -import cn.iocoder.yudao.module.ai.enums.AiModelEnum; 6 +import cn.iocoder.yudao.module.ai.enums.model.AiModelEnum;
7 import com.baomidou.mybatisplus.annotation.KeySequence; 7 import com.baomidou.mybatisplus.annotation.KeySequence;
8 import com.baomidou.mybatisplus.annotation.TableId; 8 import com.baomidou.mybatisplus.annotation.TableId;
9 import com.baomidou.mybatisplus.annotation.TableName; 9 import com.baomidou.mybatisplus.annotation.TableName;
10 import lombok.*; 10 import lombok.*;
11 11
12 import java.time.LocalDateTime; 12 import java.time.LocalDateTime;
13 -import java.util.Date;  
14 13
15 /** 14 /**
16 * AI Chat 对话 DO 15 * AI Chat 对话 DO
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; @@ -5,7 +5,7 @@ import org.springframework.ai.chat.messages.MessageType;
5 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 5 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
6 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; 6 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
7 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; 7 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
8 -import cn.iocoder.yudao.module.ai.enums.AiModelEnum; 8 +import cn.iocoder.yudao.module.ai.enums.model.AiModelEnum;
9 import com.baomidou.mybatisplus.annotation.KeySequence; 9 import com.baomidou.mybatisplus.annotation.KeySequence;
10 import com.baomidou.mybatisplus.annotation.TableName; 10 import com.baomidou.mybatisplus.annotation.TableName;
11 import lombok.*; 11 import lombok.*;
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java
1 package cn.iocoder.yudao.module.ai.dal.dataobject.image; 1 package cn.iocoder.yudao.module.ai.dal.dataobject.image;
2 2
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
  5 +import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum;
  6 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
4 import com.baomidou.mybatisplus.annotation.IdType; 7 import com.baomidou.mybatisplus.annotation.IdType;
5 import com.baomidou.mybatisplus.annotation.TableField; 8 import com.baomidou.mybatisplus.annotation.TableField;
6 import com.baomidou.mybatisplus.annotation.TableId; 9 import com.baomidou.mybatisplus.annotation.TableId;
7 import com.baomidou.mybatisplus.annotation.TableName; 10 import com.baomidou.mybatisplus.annotation.TableName;
8 import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; 11 import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
9 -import io.swagger.v3.oas.annotations.media.Schema;  
10 import lombok.Data; 12 import lombok.Data;
  13 +import org.springframework.ai.openai.OpenAiImageOptions;
  14 +import org.springframework.ai.stabilityai.api.StabilityAiImageOptions;
11 15
12 import java.util.Map; 16 import java.util.Map;
13 17
@@ -16,70 +20,86 @@ import java.util.Map; @@ -16,70 +20,86 @@ import java.util.Map;
16 * 20 *
17 * @author fansili 21 * @author fansili
18 */ 22 */
19 -@TableName("ai_image") 23 +@TableName(value = "ai_image", autoResultMap = true)
20 @Data 24 @Data
21 public class AiImageDO extends BaseDO { 25 public class AiImageDO extends BaseDO {
22 26
23 // TODO @fan:1)使用 java 注释哈,不要注解。2)关联、枚举字段,要关联到对应类,参考 AiChatMessageDO 的注释 27 // TODO @fan:1)使用 java 注释哈,不要注解。2)关联、枚举字段,要关联到对应类,参考 AiChatMessageDO 的注释
24 28
  29 + /**
  30 + * 编号
  31 + */
25 @TableId(type = IdType.AUTO) 32 @TableId(type = IdType.AUTO)
26 private Long id; 33 private Long id;
27 34
28 - @Schema(description = "用户编号") 35 + /**
  36 + * 用户编号
  37 + *
  38 + * 关联 {@link AdminUserRespDTO#getId()}
  39 + */
29 private Long userId; 40 private Long userId;
30 41
31 - @Schema(description = "midjourney proxy 关联的 job id")  
32 - private String jobId;  
33 -  
34 - @Schema(description = "提示词") 42 + /**
  43 + * 提示词
  44 + */
35 private String prompt; 45 private String prompt;
36 46
37 - @Schema(description = "平台") 47 + /**
  48 + * 平台
  49 + *
  50 + * 枚举 {@link cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum}
  51 + */
38 private String platform; 52 private String platform;
39 -  
40 - @Schema(description = "模型") 53 + /**
  54 + * 模型
  55 + *
  56 + * 冗余 {@link AiChatModelDO#getModel()}
  57 + */
41 private String model; 58 private String model;
42 59
43 - @Schema(description = "图片宽度") 60 + /**
  61 + * 图片宽度
  62 + */
44 private Integer width; 63 private Integer width;
45 -  
46 - @Schema(description = "图片高度") 64 + /**
  65 + * 图片高度
  66 + */
47 private Integer height; 67 private Integer height;
48 68
49 // TODO @fan:这种就注释绘画状态,然后枚举类关联下就好啦 69 // TODO @fan:这种就注释绘画状态,然后枚举类关联下就好啦
50 - @Schema(description = "绘画状态:提交、排队、绘画中、绘画完成、绘画失败") 70 + /**
  71 + * 生成状态
  72 + *
  73 + * 枚举 {@link AiImageStatusEnum}
  74 + */
51 private String status; 75 private String status;
52 76
53 - @Schema(description = "是否发布")  
54 - private String publicStatus;  
55 -  
56 - @Schema(description = "图片地址(自己服务器)") 77 + /**
  78 + * 图片地址
  79 + */
57 private String picUrl; 80 private String picUrl;
58 -  
59 - // TODO @芋艿:可能要删除掉  
60 - @Schema(description = "绘画图片地址(绘画好的服务器)")  
61 - private String originalPicUrl;  
62 -  
63 - // ============ 绘画请求参数 ============  
64 -  
65 /** 81 /**
66 - * - style 82 + * 是否公开
67 */ 83 */
68 - @Schema(description = "绘画请求参数")  
69 - @TableField(typeHandler = JacksonTypeHandler.class)  
70 - private Map<String, Object> drawRequest; 84 + private Boolean publicStatus;
71 85
72 /** 86 /**
73 - * - mjNonceId  
74 - * - mjOperationId  
75 - * - mjOperationName  
76 - * - mjOperations 87 + * 绘制参数,不同 platform 的不同参数
  88 + *
  89 + * 1. {@link OpenAiImageOptions}
  90 + * 2. {@link StabilityAiImageOptions}
77 */ 91 */
78 - @Schema(description = "绘画请求响应参数")  
79 @TableField(typeHandler = JacksonTypeHandler.class) 92 @TableField(typeHandler = JacksonTypeHandler.class)
80 - private Map<String, Object> drawResponse; 93 + private Map<String, String> options;
81 94
82 - @Schema(description = "绘画错误信息") 95 + // TODO @芋艿:再瞅瞅
  96 + /**
  97 + * midjourney proxy 关联的 job id
  98 + */
  99 + private String jobId;
  100 + /**
  101 + * 绘画错误信息
  102 + */
83 private String errorMessage; 103 private String errorMessage;
84 104
85 } 105 }
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; @@ -25,8 +25,8 @@ import java.util.List;
25 25
26 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 26 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
27 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 27 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
28 -import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.CHAT_CONVERSATION_MODEL_ERROR;  
29 -import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS; 28 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_MODEL_ERROR;
  29 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS;
30 30
31 /** 31 /**
32 * AI 聊天对话 Service 实现类 32 * AI 聊天对话 Service 实现类
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; @@ -8,7 +8,7 @@ import cn.iocoder.yudao.framework.ai.core.factory.AiClientFactory;
8 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 8 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
9 import cn.iocoder.yudao.framework.common.pojo.PageResult; 9 import cn.iocoder.yudao.framework.common.pojo.PageResult;
10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
11 -import cn.iocoder.yudao.module.ai.ErrorCodeConstants; 11 +import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;
12 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO; 12 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;
13 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO; 13 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;
14 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO; 14 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 @@ -39,8 +39,8 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
39 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error; 39 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
40 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 40 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
41 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 41 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
42 -import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.AI_CHAT_MESSAGE_NOT_EXIST;  
43 -import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS; 42 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.AI_CHAT_MESSAGE_NOT_EXIST;
  43 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS;
44 44
45 /** 45 /**
46 * AI 聊天消息 Service 实现类 46 * AI 聊天消息 Service 实现类
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; @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.ai.service.image;
3 import cn.iocoder.yudao.framework.common.pojo.PageParam; 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.module.ai.client.vo.MidjourneyNotifyReqVO; 5 import cn.iocoder.yudao.module.ai.client.vo.MidjourneyNotifyReqVO;
6 -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallReqVO; 6 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO;
7 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO; 7 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO;
8 import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; 8 import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
9 9
@@ -32,12 +32,12 @@ public interface AiImageService { @@ -32,12 +32,12 @@ public interface AiImageService {
32 AiImageDO getImage(Long id); 32 AiImageDO getImage(Long id);
33 33
34 /** 34 /**
35 - * ai绘画 - dall2/dall3 绘画 35 + * 绘制图片
36 * 36 *
37 - * @param loginUserId  
38 - * @param req 37 + * @param userId 用户编号
  38 + * @param drawReqVO 绘制请求
39 */ 39 */
40 - Long dall(Long loginUserId, AiImageDallReqVO req); 40 + Long drawImage(Long userId, AiImageDrawReqVO drawReqVO);
41 41
42 /** 42 /**
43 * midjourney 图片生成 43 * midjourney 图片生成
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java
1 package cn.iocoder.yudao.module.ai.service.image; 1 package cn.iocoder.yudao.module.ai.service.image;
2 2
3 -import cn.hutool.core.bean.BeanUtil;  
4 import cn.hutool.core.codec.Base64; 3 import cn.hutool.core.codec.Base64;
  4 +import cn.hutool.core.map.MapUtil;
5 import cn.hutool.core.util.ObjUtil; 5 import cn.hutool.core.util.ObjUtil;
6 import cn.hutool.core.util.StrUtil; 6 import cn.hutool.core.util.StrUtil;
7 import cn.hutool.extra.spring.SpringUtil; 7 import cn.hutool.extra.spring.SpringUtil;
8 import cn.hutool.http.HttpUtil; 8 import cn.hutool.http.HttpUtil;
9 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; 9 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
10 -import cn.iocoder.yudao.framework.ai.core.enums.OpenAiImageModelEnum;  
11 -import cn.iocoder.yudao.framework.ai.core.enums.OpenAiImageStyleEnum;  
12 import cn.iocoder.yudao.framework.ai.core.factory.AiClientFactory; 10 import cn.iocoder.yudao.framework.ai.core.factory.AiClientFactory;
13 import cn.iocoder.yudao.framework.common.pojo.PageParam; 11 import cn.iocoder.yudao.framework.common.pojo.PageParam;
14 import cn.iocoder.yudao.framework.common.pojo.PageResult; 12 import cn.iocoder.yudao.framework.common.pojo.PageResult;
15 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 13 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
16 -import cn.iocoder.yudao.module.ai.AiCommonConstants;  
17 import cn.iocoder.yudao.module.ai.client.MidjourneyProxyClient; 14 import cn.iocoder.yudao.module.ai.client.MidjourneyProxyClient;
18 import cn.iocoder.yudao.module.ai.client.enums.MidjourneyModelEnum; 15 import cn.iocoder.yudao.module.ai.client.enums.MidjourneyModelEnum;
19 import cn.iocoder.yudao.module.ai.client.enums.MidjourneySubmitCodeEnum; 16 import cn.iocoder.yudao.module.ai.client.enums.MidjourneySubmitCodeEnum;
@@ -21,17 +18,18 @@ import cn.iocoder.yudao.module.ai.client.enums.MidjourneyTaskStatusEnum; @@ -21,17 +18,18 @@ import cn.iocoder.yudao.module.ai.client.enums.MidjourneyTaskStatusEnum;
21 import cn.iocoder.yudao.module.ai.client.vo.MidjourneyImagineReqVO; 18 import cn.iocoder.yudao.module.ai.client.vo.MidjourneyImagineReqVO;
22 import cn.iocoder.yudao.module.ai.client.vo.MidjourneyNotifyReqVO; 19 import cn.iocoder.yudao.module.ai.client.vo.MidjourneyNotifyReqVO;
23 import cn.iocoder.yudao.module.ai.client.vo.MidjourneySubmitRespVO; 20 import cn.iocoder.yudao.module.ai.client.vo.MidjourneySubmitRespVO;
24 -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallReqVO; 21 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO;
25 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO; 22 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO;
26 import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; 23 import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
27 import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper; 24 import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper;
28 -import cn.iocoder.yudao.module.ai.enums.AiImagePublicStatusEnum;  
29 -import cn.iocoder.yudao.module.ai.enums.AiImageStatusEnum; 25 +import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum;
30 import cn.iocoder.yudao.module.infra.api.file.FileApi; 26 import cn.iocoder.yudao.module.infra.api.file.FileApi;
31 -import com.google.common.collect.ImmutableMap;  
32 import jakarta.annotation.Resource; 27 import jakarta.annotation.Resource;
33 import lombok.extern.slf4j.Slf4j; 28 import lombok.extern.slf4j.Slf4j;
34 -import org.springframework.ai.image.*; 29 +import org.springframework.ai.image.ImageClient;
  30 +import org.springframework.ai.image.ImageOptions;
  31 +import org.springframework.ai.image.ImagePrompt;
  32 +import org.springframework.ai.image.ImageResponse;
35 import org.springframework.ai.openai.OpenAiImageOptions; 33 import org.springframework.ai.openai.OpenAiImageOptions;
36 import org.springframework.ai.stabilityai.api.StabilityAiImageOptions; 34 import org.springframework.ai.stabilityai.api.StabilityAiImageOptions;
37 import org.springframework.beans.factory.annotation.Autowired; 35 import org.springframework.beans.factory.annotation.Autowired;
@@ -41,7 +39,7 @@ import org.springframework.stereotype.Service; @@ -41,7 +39,7 @@ import org.springframework.stereotype.Service;
41 import org.springframework.transaction.annotation.Transactional; 39 import org.springframework.transaction.annotation.Transactional;
42 40
43 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 41 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
44 -import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.AI_IMAGE_NOT_EXISTS; 42 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.AI_IMAGE_NOT_EXISTS;
45 43
46 /** 44 /**
47 * AI 绘画 Service 实现类 45 * AI 绘画 Service 实现类
@@ -78,22 +76,18 @@ public class AiImageServiceImpl implements AiImageService { @@ -78,22 +76,18 @@ public class AiImageServiceImpl implements AiImageService {
78 } 76 }
79 77
80 @Override 78 @Override
81 - public Long dall(Long userId, AiImageDallReqVO req) {  
82 - req.setPlatform("dall"); // TODO 芋艿:临时写死 79 + public Long drawImage(Long userId, AiImageDrawReqVO drawReqVO) {
83 // 1. 保存数据库 80 // 1. 保存数据库
84 - AiImageDO image = BeanUtils.toBean(req, AiImageDO.class)  
85 - .setUserId(userId).setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus())  
86 - .setWidth(req.getWidth()).setHeight(req.getHeight())  
87 - .setDrawRequest(ImmutableMap.of(AiCommonConstants.DRAW_REQ_KEY_STYLE, req.getStyle()))  
88 - .setPublicStatus(AiImagePublicStatusEnum.PRIVATE.getStatus()); 81 + AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false)
  82 + .setWidth(drawReqVO.getWidth()).setHeight(drawReqVO.getHeight()).setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus());
89 imageMapper.insert(image); 83 imageMapper.insert(image);
90 - // 2. 异步绘制,后续前端通过返回的 id 进行伦旭  
91 - getSelf().doDall(image, req); 84 + // 2. 异步绘制,后续前端通过返回的 id 进行轮询结果
  85 + getSelf().doDall(image, drawReqVO);
92 return image.getId(); 86 return image.getId();
93 } 87 }
94 88
95 @Async 89 @Async
96 - public void doDall(AiImageDO image, AiImageDallReqVO req) { 90 + public void doDall(AiImageDO image, AiImageDrawReqVO req) {
97 try { 91 try {
98 // 1.1 构建请求 92 // 1.1 构建请求
99 ImageOptions request = buildImageOptions(req); 93 ImageOptions request = buildImageOptions(req);
@@ -106,7 +100,7 @@ public class AiImageServiceImpl implements AiImageService { @@ -106,7 +100,7 @@ public class AiImageServiceImpl implements AiImageService {
106 String filePath = fileApi.createFile(fileContent); 100 String filePath = fileApi.createFile(fileContent);
107 101
108 // 3. 更新数据库 102 // 3. 更新数据库
109 - imageMapper.updateById(new AiImageDO().setId(image.getId()).setStatus(AiImageStatusEnum.COMPLETE.getStatus()) 103 + imageMapper.updateById(new AiImageDO().setId(image.getId()).setStatus(AiImageStatusEnum.SUCCESS.getStatus())
110 .setPicUrl(filePath)); 104 .setPicUrl(filePath));
111 } catch (Exception ex) { 105 } catch (Exception ex) {
112 log.error("[doDall][image({}) 生成异常]", image, ex); 106 log.error("[doDall][image({}) 生成异常]", image, ex);
@@ -115,30 +109,28 @@ public class AiImageServiceImpl implements AiImageService { @@ -115,30 +109,28 @@ public class AiImageServiceImpl implements AiImageService {
115 } 109 }
116 } 110 }
117 111
118 - private static ImageOptions buildImageOptions(AiImageDallReqVO draw) {  
119 - if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.OPEN_AI_DALL.getPlatform())) {  
120 - OpenAiImageOptions request = new OpenAiImageOptions();  
121 - request.setModel(OpenAiImageModelEnum.valueOfModel(draw.getModel()).getModel());  
122 - request.setStyle(OpenAiImageStyleEnum.valueOfStyle(draw.getStyle()).getStyle());  
123 - request.setSize(String.format(AiCommonConstants.DALL_SIZE_TEMPLATE, draw.getWidth(), draw.getHeight()));  
124 - request.setResponseFormat("b64_json");  
125 - return request;  
126 - } else {  
127 - // https://platform.stability.ai/docs/api-reference#tag/Generate/paths/~1v2beta~1stable-image~1generate~1sd3/post  
128 - return StabilityAiImageOptions.builder().withModel(draw.getModel()) 112 + private static ImageOptions buildImageOptions(AiImageDrawReqVO draw) {
  113 + if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.OPENAI.getPlatform())) {
  114 + // https://platform.openai.com/docs/api-reference/images/create
  115 + return OpenAiImageOptions.builder().withModel(draw.getModel())
129 .withHeight(draw.getHeight()).withWidth(draw.getWidth()) 116 .withHeight(draw.getHeight()).withWidth(draw.getWidth())
  117 + .withStyle(MapUtil.getStr(draw.getOptions(), "style")) // 风格
  118 + .withResponseFormat("b64_json")
  119 + .build();
  120 + } else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.STABLE_DIFFUSION.getPlatform())) {
  121 + // https://platform.stability.ai/docs/api-reference#tag/Text-to-Image/operation/textToImage
  122 + return StabilityAiImageOptions.builder().withModel(draw.getModel())
  123 + .withHeight(draw.getHeight()).withWidth(draw.getWidth()) // TODO @芋艿:各种参数
130 .build(); 124 .build();
131 } 125 }
132 -// return null; 126 + throw new IllegalArgumentException("不支持的 AI 平台:" + draw.getPlatform());
133 } 127 }
134 128
135 @Override 129 @Override
136 @Transactional(rollbackFor = Exception.class) 130 @Transactional(rollbackFor = Exception.class)
137 public Long midjourneyImagine(Long loginUserId, AiImageMidjourneyImagineReqVO req) { 131 public Long midjourneyImagine(Long loginUserId, AiImageMidjourneyImagineReqVO req) {
138 -  
139 // 1、构建 AiImageDO 132 // 1、构建 AiImageDO
140 AiImageDO aiImageDO = new AiImageDO(); 133 AiImageDO aiImageDO = new AiImageDO();
141 - aiImageDO.setId(null);  
142 aiImageDO.setUserId(loginUserId); 134 aiImageDO.setUserId(loginUserId);
143 aiImageDO.setPrompt(req.getPrompt()); 135 aiImageDO.setPrompt(req.getPrompt());
144 aiImageDO.setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform()); 136 aiImageDO.setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform());
@@ -147,12 +139,6 @@ public class AiImageServiceImpl implements AiImageService { @@ -147,12 +139,6 @@ public class AiImageServiceImpl implements AiImageService {
147 aiImageDO.setWidth(null); 139 aiImageDO.setWidth(null);
148 aiImageDO.setHeight(null); 140 aiImageDO.setHeight(null);
149 aiImageDO.setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()); 141 aiImageDO.setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus());
150 - aiImageDO.setPublicStatus(AiImagePublicStatusEnum.PRIVATE.getStatus());  
151 - aiImageDO.setPicUrl(null);  
152 - aiImageDO.setOriginalPicUrl(null);  
153 - aiImageDO.setDrawRequest(null);  
154 - aiImageDO.setDrawResponse(null);  
155 - aiImageDO.setErrorMessage(null);  
156 142
157 // 2、保存 image 143 // 2、保存 image
158 imageMapper.insert(aiImageDO); 144 imageMapper.insert(aiImageDO);
@@ -211,7 +197,7 @@ public class AiImageServiceImpl implements AiImageService { @@ -211,7 +197,7 @@ public class AiImageServiceImpl implements AiImageService {
211 // 197 //
212 String imageStatus = null; 198 String imageStatus = null;
213 if (MidjourneyTaskStatusEnum.SUCCESS == notifyReqVO.getStatus()) { 199 if (MidjourneyTaskStatusEnum.SUCCESS == notifyReqVO.getStatus()) {
214 - imageStatus = AiImageStatusEnum.COMPLETE.getStatus(); 200 + imageStatus = AiImageStatusEnum.SUCCESS.getStatus();
215 } else if (MidjourneyTaskStatusEnum.FAILURE == notifyReqVO.getStatus()) { 201 } else if (MidjourneyTaskStatusEnum.FAILURE == notifyReqVO.getStatus()) {
216 imageStatus = AiImageStatusEnum.FAIL.getStatus(); 202 imageStatus = AiImageStatusEnum.FAIL.getStatus();
217 } 203 }
@@ -226,8 +212,7 @@ public class AiImageServiceImpl implements AiImageService { @@ -226,8 +212,7 @@ public class AiImageServiceImpl implements AiImageService {
226 .setId(image.getId()) 212 .setId(image.getId())
227 .setStatus(imageStatus) 213 .setStatus(imageStatus)
228 .setPicUrl(filePath) 214 .setPicUrl(filePath)
229 - .setOriginalPicUrl(notifyReqVO.getImageUrl())  
230 - .setDrawResponse(BeanUtil.beanToMap(notifyReqVO)) 215 +// .setOriginalPicUrl(notifyReqVO.getImageUrl()) TODO @fan:就不存原始的图片地址啦
231 ); 216 );
232 return true; 217 return true;
233 } 218 }
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; @@ -5,7 +5,7 @@ import cn.hutool.core.util.StrUtil;
5 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyOperationsVO; 5 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyOperationsVO;
6 import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; 6 import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
7 import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper; 7 import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper;
8 -import cn.iocoder.yudao.module.ai.enums.AiImageStatusEnum; 8 +import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum;
9 import com.alibaba.fastjson.JSON; 9 import com.alibaba.fastjson.JSON;
10 import lombok.AllArgsConstructor; 10 import lombok.AllArgsConstructor;
11 import lombok.extern.slf4j.Slf4j; 11 import lombok.extern.slf4j.Slf4j;
@@ -89,7 +89,7 @@ public class YuDaoMidjourneyMessageHandler implements MidjourneyMessageHandler { @@ -89,7 +89,7 @@ public class YuDaoMidjourneyMessageHandler implements MidjourneyMessageHandler {
89 AiImageStatusEnum drawingStatusEnum = null; 89 AiImageStatusEnum drawingStatusEnum = null;
90 String generateStatus = midjourneyMessage.getGenerateStatus(); 90 String generateStatus = midjourneyMessage.getGenerateStatus();
91 if (MidjourneyGennerateStatusEnum.COMPLETED.getStatus().equals(generateStatus)) { 91 if (MidjourneyGennerateStatusEnum.COMPLETED.getStatus().equals(generateStatus)) {
92 - drawingStatusEnum = AiImageStatusEnum.COMPLETE; 92 + drawingStatusEnum = AiImageStatusEnum.SUCCESS;
93 } else if (MidjourneyGennerateStatusEnum.IN_PROGRESS.getStatus().equals(generateStatus)) { 93 } else if (MidjourneyGennerateStatusEnum.IN_PROGRESS.getStatus().equals(generateStatus)) {
94 drawingStatusEnum = AiImageStatusEnum.IN_PROGRESS; 94 drawingStatusEnum = AiImageStatusEnum.IN_PROGRESS;
95 } 95 }
@@ -101,7 +101,6 @@ public class YuDaoMidjourneyMessageHandler implements MidjourneyMessageHandler { @@ -101,7 +101,6 @@ public class YuDaoMidjourneyMessageHandler implements MidjourneyMessageHandler {
101 // 更新数据库 101 // 更新数据库
102 aiImageMapper.updateByMjNonce(nonceId, 102 aiImageMapper.updateByMjNonce(nonceId,
103 new AiImageDO() 103 new AiImageDO()
104 - .setOriginalPicUrl(imageUrl)  
105 .setStatus(drawingStatusEnum == null ? null : drawingStatusEnum.getStatus()) 104 .setStatus(drawingStatusEnum == null ? null : drawingStatusEnum.getStatus())
106 // .setMjNonceId(midjourneyMessage.getId()) 105 // .setMjNonceId(midjourneyMessage.getId())
107 // .setMjOperations(JsonUtils.toJsonString(midjourneyOperations)) 106 // .setMjOperations(JsonUtils.toJsonString(midjourneyOperations))
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalChatConfigVO.java deleted
1 -package cn.iocoder.yudao.module.ai.service.image.midjourneyHandler.vo;  
2 -  
3 -import io.swagger.v3.oas.annotations.media.Schema;  
4 -import jakarta.validation.constraints.NotNull;  
5 -import lombok.Data;  
6 -import lombok.experimental.Accessors;  
7 -  
8 -/**  
9 - * chat config  
10 - *  
11 - * @author fansili  
12 - * @time 2024/5/6 15:06  
13 - * @since 1.0  
14 - */  
15 -@Data  
16 -@Accessors(chain = true)  
17 -public class AiChatModalChatConfigVO extends AiChatModalConfigVO {  
18 -  
19 - @NotNull  
20 - @Schema(description = "在生成消息时采用的Top-K采样大小")  
21 - private Double topK;  
22 -  
23 - @NotNull  
24 - @Schema(description = "Top-P核采样方法的概率阈值")  
25 - private Double topP;  
26 -  
27 - @NotNull  
28 - @Schema(description = "温度参数,用于调整生成回复的随机性和多样性程度")  
29 - private Double temperature;  
30 -  
31 - @NotNull  
32 - @Schema(description = "最大 tokens")  
33 - private Integer maxTokens;  
34 -  
35 -}  
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalConfigVO.java deleted
1 -package cn.iocoder.yudao.module.ai.service.image.midjourneyHandler.vo;  
2 -  
3 -import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;  
4 -import lombok.Data;  
5 -import lombok.experimental.Accessors;  
6 -import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel;  
7 -import cn.iocoder.yudao.framework.ai.core.model.yiyan.api.YiYanChatModel;  
8 -  
9 -/**  
10 - * modal config  
11 - *  
12 - * @author fansili  
13 - * @time 2024/5/6 15:06  
14 - * @since 1.0  
15 - */  
16 -@Data  
17 -@Accessors(chain = true)  
18 -public class AiChatModalConfigVO {  
19 -  
20 - /**  
21 - * 模型平台 (冗余,方便类型转换)  
22 - * 参考:{@link AiPlatformEnum}  
23 - */  
24 - private String platform;  
25 - /**  
26 - * 模型类型(冗余,方便类型转换)  
27 - * {@link YiYanChatModel}  
28 - * {@link XingHuoChatModel}  
29 - */  
30 - private String type;  
31 -}  
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalDallConfigVO.java deleted
1 -package cn.iocoder.yudao.module.ai.service.image.midjourneyHandler.vo;  
2 -  
3 -import cn.iocoder.yudao.framework.ai.core.enums.OpenAiImageStyleEnum;  
4 -import lombok.Data;  
5 -import lombok.experimental.Accessors;  
6 -  
7 -/**  
8 - * dall  
9 - *  
10 - * @author fansili  
11 - * @time 2024/5/6 15:06  
12 - * @since 1.0  
13 - */  
14 -@Data  
15 -@Accessors(chain = true)  
16 -public class AiChatModalDallConfigVO extends AiChatModalConfigVO {  
17 - // 可选字段,默认为1  
18 - // 生成图像的数量,必须在1到10之间。对于dall-e-3模型,目前仅支持n=1。  
19 - private Integer n = 1;  
20 -  
21 - // 可选字段,默认为standard  
22 - // 设置生成图像的质量。hd质量将创建细节更丰富、图像整体一致性更高的图片。该参数仅对dall-e-3模型有效。  
23 - private String quality = "standard";  
24 -  
25 - // 可选字段,默认为url  
26 - // 返回生成图像的格式。必须是url或b64_json中的一种。URL链接的有效期是从生成图像后开始计算的60分钟内有效。  
27 - private String responseFormat = "url";  
28 -  
29 - // 可选字段,默认为1024x1024  
30 - // 生成图像的尺寸大小。对于dall-e-2模型,尺寸可为256x256, 512x512, 或 1024x1024。对于dall-e-3模型,尺寸可为1024x1024, 1792x1024, 或 1024x1792。  
31 - private String size = "1024x1024";  
32 -  
33 - // 可选字段,默认为vivid  
34 - // 图像生成的风格。可为vivid(生动)或natural(自然)。vivid会使模型偏向生成超现实和戏剧性的图像,而natural则会让模型产出更自然、不那么超现实的图像。该参数仅对dall-e-3模型有效。  
35 - private OpenAiImageStyleEnum style = OpenAiImageStyleEnum.VIVID;  
36 -  
37 - // 可选字段  
38 - // 代表您的终端用户的唯一标识符,有助于OpenAI监控并检测滥用行为。了解更多信息请参考官方文档。  
39 - private String endUserId = "UID123456";  
40 -  
41 -}  
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/midjourneyHandler/vo/AiChatModalMidjourneyConfigVO.java deleted
1 -package cn.iocoder.yudao.module.ai.service.image.midjourneyHandler.vo;  
2 -  
3 -import lombok.Data;  
4 -import lombok.experimental.Accessors;  
5 -  
6 -/**  
7 - * Midjourney Config  
8 - *  
9 - * @author fansili  
10 - * @time 2024/5/6 15:07  
11 - * @since 1.0  
12 - */  
13 -@Data  
14 -@Accessors(chain = true)  
15 -public class AiChatModalMidjourneyConfigVO extends AiChatModalConfigVO {  
16 -}  
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; @@ -17,7 +17,7 @@ import org.springframework.validation.annotation.Validated;
17 import java.util.List; 17 import java.util.List;
18 18
19 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 19 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
20 -import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.*; 20 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
21 21
22 /** 22 /**
23 * AI API 密钥 Service 实现类 23 * AI API 密钥 Service 实现类
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; @@ -16,7 +16,7 @@ import java.util.Collection;
16 import java.util.List; 16 import java.util.List;
17 17
18 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 18 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
19 -import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.*; 19 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
20 20
21 /** 21 /**
22 * AI 聊天模型 Service 实现类 22 * AI 聊天模型 Service 实现类
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; @@ -21,7 +21,7 @@ import java.util.List;
21 21
22 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 22 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
23 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 23 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
24 -import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.*; 24 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
25 25
26 /** 26 /**
27 * AI 聊天角色 Service 实现类 27 * AI 聊天角色 Service 实现类
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; @@ -3,10 +3,9 @@ package cn.iocoder.yudao.framework.ai.config;
3 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; 3 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
4 import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel; 4 import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel;
5 import cn.iocoder.yudao.framework.ai.core.model.yiyan.api.YiYanChatModel; 5 import cn.iocoder.yudao.framework.ai.core.model.yiyan.api.YiYanChatModel;
6 -import cn.iocoder.yudao.framework.ai.core.enums.OpenAiImageModelEnum;  
7 -import cn.iocoder.yudao.framework.ai.core.enums.OpenAiImageStyleEnum;  
8 import lombok.Data; 6 import lombok.Data;
9 import lombok.experimental.Accessors; 7 import lombok.experimental.Accessors;
  8 +import org.springframework.ai.autoconfigure.openai.OpenAiImageProperties;
10 import org.springframework.boot.context.properties.ConfigurationProperties; 9 import org.springframework.boot.context.properties.ConfigurationProperties;
11 10
12 /** 11 /**
@@ -94,27 +93,6 @@ public class YudaoAiProperties { @@ -94,27 +93,6 @@ public class YudaoAiProperties {
94 93
95 @Data 94 @Data
96 @Accessors(chain = true) 95 @Accessors(chain = true)
97 - public static class OpenAiImageProperties {  
98 -  
99 - private boolean enable = false;  
100 -  
101 - /**  
102 - * api key  
103 - */  
104 - private String apiKey;  
105 - /**  
106 - * 模型  
107 - */  
108 - private OpenAiImageModelEnum model = OpenAiImageModelEnum.DALL_E_2;  
109 - /**  
110 - * 风格  
111 - */  
112 - private OpenAiImageStyleEnum style = OpenAiImageStyleEnum.VIVID;  
113 -  
114 - }  
115 -  
116 - @Data  
117 - @Accessors(chain = true)  
118 public static class MidjourneyProperties { 96 public static class MidjourneyProperties {
119 private boolean enable = false; 97 private boolean enable = false;
120 98
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 { @@ -20,7 +20,6 @@ public enum AiPlatformEnum {
20 QIAN_WEN("QianWen", "千问"), // 阿里 20 QIAN_WEN("QianWen", "千问"), // 阿里
21 GEMIR ("gemir ", "gemir "), // 谷歌 21 GEMIR ("gemir ", "gemir "), // 谷歌
22 22
23 - OPEN_AI_DALL("dall", "dall"), // TODO OpenAI 提供的绘图,接入中;TODO 要不要统一下?!  
24 STABLE_DIFFUSION("StableDiffusion", "StableDiffusion"), // Stability AI 23 STABLE_DIFFUSION("StableDiffusion", "StableDiffusion"), // Stability AI
25 MIDJOURNEY("midjourney", "midjourney"), // TODO MJ 提供的绘图,接入中 24 MIDJOURNEY("midjourney", "midjourney"), // TODO MJ 提供的绘图,接入中
26 ; 25 ;
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/OpenAiImageModelEnum.java deleted
1 -package cn.iocoder.yudao.framework.ai.core.enums;  
2 -  
3 -import lombok.AllArgsConstructor;  
4 -import lombok.Getter;  
5 -  
6 -// TODO 芋艿:待梳理  
7 -/**  
8 - * open ai  
9 - *  
10 - * @author fansili  
11 - * @time 2024/4/28 14:21  
12 - * @since 1.0  
13 - */  
14 -@AllArgsConstructor  
15 -@Getter  
16 -@Deprecated  
17 -public enum OpenAiImageModelEnum {  
18 -  
19 - DALL_E_2("dall-e-2", "dall-e-2"),  
20 -  
21 - DALL_E_3("dall-e-3", "dall-e-3")  
22 -  
23 - ;  
24 -  
25 - private String model;  
26 -  
27 - private String name;  
28 -  
29 - public static OpenAiImageModelEnum valueOfModel(String model) {  
30 - for (OpenAiImageModelEnum itemEnum : OpenAiImageModelEnum.values()) {  
31 - if (itemEnum.getModel().equals(model)) {  
32 - return itemEnum;  
33 - }  
34 - }  
35 - throw new IllegalArgumentException("Invalid MessageType value: " + model);  
36 - }  
37 -}  
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/OpenAiImageStyleEnum.java deleted
1 -package cn.iocoder.yudao.framework.ai.core.enums;  
2 -  
3 -import lombok.AllArgsConstructor;  
4 -import lombok.Getter;  
5 -  
6 -// TODO 芋艿:待梳理  
7 -/**  
8 - * open ai image style  
9 - *  
10 - * @author fansili  
11 - * @time 2024/4/28 16:15  
12 - * @since 1.0  
13 - */  
14 -@AllArgsConstructor  
15 -@Getter  
16 -@Deprecated  
17 -public enum OpenAiImageStyleEnum {  
18 -  
19 - // 图像生成的风格。可为vivid(生动)或 natural(自然)。vivid会使模型偏向生成超现实和戏剧性的图像,而natural则会让模型产出更自然、不那么超现实的图像。该参数仅对dall-e-3模型有效。  
20 -  
21 - VIVID("vivid", "生动"),  
22 - NATURAL("natural", "自然"),  
23 -  
24 - ;  
25 -  
26 - private String style;  
27 -  
28 - private String name;  
29 -  
30 - public static OpenAiImageStyleEnum valueOfStyle(String style) {  
31 - for (OpenAiImageStyleEnum itemEnum : OpenAiImageStyleEnum.values()) {  
32 - if (itemEnum.getStyle().equals(style)) {  
33 - return itemEnum;  
34 - }  
35 - }  
36 - throw new IllegalArgumentException("Invalid MessageType value: " + style);  
37 - }  
38 -}  
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; @@ -33,6 +33,7 @@ import org.springframework.ai.openai.OpenAiChatOptions;
33 import org.springframework.ai.openai.OpenAiImageClient; 33 import org.springframework.ai.openai.OpenAiImageClient;
34 import org.springframework.ai.openai.api.ApiUtils; 34 import org.springframework.ai.openai.api.ApiUtils;
35 import org.springframework.ai.openai.api.OpenAiApi; 35 import org.springframework.ai.openai.api.OpenAiApi;
  36 +import org.springframework.ai.stabilityai.StabilityAiImageClient;
36 37
37 import java.util.List; 38 import java.util.List;
38 39
@@ -88,12 +89,15 @@ public class AiClientFactoryImpl implements AiClientFactory { @@ -88,12 +89,15 @@ public class AiClientFactoryImpl implements AiClientFactory {
88 89
89 @Override 90 @Override
90 public ImageClient getDefaultImageClient(AiPlatformEnum platform) { 91 public ImageClient getDefaultImageClient(AiPlatformEnum platform) {
  92 + //noinspection EnhancedSwitchMigration
91 switch (platform) { 93 switch (platform) {
92 - case OPEN_AI_DALL: 94 + case OPENAI:
93 return SpringUtil.getBean(OpenAiImageClient.class); 95 return SpringUtil.getBean(OpenAiImageClient.class);
94 - 96 + case STABLE_DIFFUSION:
  97 + return SpringUtil.getBean(StabilityAiImageClient.class);
  98 + default:
  99 + throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform));
95 } 100 }
96 - return null;  
97 } 101 }
98 102
99 private static String buildClientCacheKey(Class<?> clazz, Object... params) { 103 private static String buildClientCacheKey(Class<?> clazz, Object... params) {