Commit b4eed07d619ee75d4a4e2e921a386a6a9ba0e869

Authored by YunaiV
1 parent 4c3add50

【代码优化】AI:MJ 配置类的简化

yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/config/YudaoMidjourneyConfiguration.java deleted
1 -package cn.iocoder.yudao.module.ai.config;  
2 -  
3 -import cn.iocoder.yudao.framework.ai.core.model.midjourney.MidjourneyConfig;  
4 -import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;  
5 -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;  
6 -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;  
7 -import org.springframework.context.annotation.Bean;  
8 -import org.springframework.context.annotation.Configuration;  
9 -  
10 -/**  
11 - * 配置  
12 - *  
13 - * @author fansili  
14 - * @time 2024/6/13 09:50  
15 - */  
16 -@Configuration  
17 -public class YudaoMidjourneyConfiguration {  
18 -  
19 - @Bean  
20 - @ConditionalOnProperty(value = "ai.midjourney-proxy.enable", havingValue = "true")  
21 - public MidjourneyApi midjourneyApi(YudaoMidjourneyProperties midjourneyProperties) {  
22 - return new MidjourneyApi(BeanUtils.toBean(midjourneyProperties, MidjourneyConfig.class));  
23 - }  
24 -}  
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/config/YudaoMidjourneyProperties.java deleted
1 -package cn.iocoder.yudao.module.ai.config;  
2 -  
3 -import lombok.Data;  
4 -import org.springframework.boot.context.properties.ConfigurationProperties;  
5 -import org.springframework.context.annotation.Configuration;  
6 -  
7 -/**  
8 - * Midjourney 属性  
9 - *  
10 - * @author fansili  
11 - * @time 2024/6/5 15:02  
12 - * @since 1.0  
13 - */  
14 -@Configuration  
15 -@ConfigurationProperties(prefix = "ai.midjourney-proxy")  
16 -@Data  
17 -public class YudaoMidjourneyProperties {  
18 -  
19 - private String enable;  
20 - private String key;  
21 - private String url;  
22 -}  
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java
@@ -29,7 +29,6 @@ import org.springframework.ai.image.ImagePrompt; @@ -29,7 +29,6 @@ import org.springframework.ai.image.ImagePrompt;
29 import org.springframework.ai.image.ImageResponse; 29 import org.springframework.ai.image.ImageResponse;
30 import org.springframework.ai.openai.OpenAiImageOptions; 30 import org.springframework.ai.openai.OpenAiImageOptions;
31 import org.springframework.ai.stabilityai.api.StabilityAiImageOptions; 31 import org.springframework.ai.stabilityai.api.StabilityAiImageOptions;
32 -import org.springframework.beans.factory.annotation.Value;  
33 import org.springframework.scheduling.annotation.Async; 32 import org.springframework.scheduling.annotation.Async;
34 import org.springframework.stereotype.Service; 33 import org.springframework.stereotype.Service;
35 import org.springframework.transaction.annotation.Transactional; 34 import org.springframework.transaction.annotation.Transactional;
@@ -63,9 +62,6 @@ public class AiImageServiceImpl implements AiImageService { @@ -63,9 +62,6 @@ public class AiImageServiceImpl implements AiImageService {
63 @Resource 62 @Resource
64 private MidjourneyApi midjourneyApi; 63 private MidjourneyApi midjourneyApi;
65 64
66 - @Value("${ai.midjourney-proxy.notifyUrl:http://127.0.0.1:48080/admin-api/ai/image/midjourney-notify}")  
67 - private String midjourneyNotifyUrl;  
68 -  
69 @Override 65 @Override
70 public PageResult<AiImageDO> getImagePageMy(Long userId, PageParam pageReqVO) { 66 public PageResult<AiImageDO> getImagePageMy(Long userId, PageParam pageReqVO) {
71 return imageMapper.selectPage(userId, pageReqVO); 67 return imageMapper.selectPage(userId, pageReqVO);
@@ -159,7 +155,7 @@ public class AiImageServiceImpl implements AiImageService { @@ -159,7 +155,7 @@ public class AiImageServiceImpl implements AiImageService {
159 155
160 // 2. 调用 Midjourney Proxy 提交任务 156 // 2. 调用 Midjourney Proxy 提交任务
161 MidjourneyApi.ImagineRequest imagineRequest = new MidjourneyApi.ImagineRequest( 157 MidjourneyApi.ImagineRequest imagineRequest = new MidjourneyApi.ImagineRequest(
162 - null, midjourneyNotifyUrl, reqVO.getPrompt(), 158 + null, reqVO.getPrompt(),null,
163 MidjourneyApi.ImagineRequest.buildState(reqVO.getWidth(), reqVO.getHeight(), reqVO.getVersion(), reqVO.getModel())); 159 MidjourneyApi.ImagineRequest.buildState(reqVO.getWidth(), reqVO.getHeight(), reqVO.getVersion(), reqVO.getModel()));
164 MidjourneyApi.SubmitResponse imagineResponse = midjourneyApi.imagine(imagineRequest); 160 MidjourneyApi.SubmitResponse imagineResponse = midjourneyApi.imagine(imagineRequest);
165 161
@@ -258,7 +254,7 @@ public class AiImageServiceImpl implements AiImageService { @@ -258,7 +254,7 @@ public class AiImageServiceImpl implements AiImageService {
258 254
259 // 2. 调用 Midjourney Proxy 提交任务 255 // 2. 调用 Midjourney Proxy 提交任务
260 MidjourneyApi.SubmitResponse actionResponse = midjourneyApi.action( 256 MidjourneyApi.SubmitResponse actionResponse = midjourneyApi.action(
261 - new MidjourneyApi.ActionRequest(button.customId(), image.getTaskId(), midjourneyNotifyUrl)); 257 + new MidjourneyApi.ActionRequest(button.customId(), image.getTaskId(), null));
262 if (!MidjourneyApi.SubmitCodeEnum.SUCCESS_CODES.contains(actionResponse.code())) { 258 if (!MidjourneyApi.SubmitCodeEnum.SUCCESS_CODES.contains(actionResponse.code())) {
263 String description = actionResponse.description().contains("quota_not_enough") ? 259 String description = actionResponse.description().contains("quota_not_enough") ?
264 "账户余额不足" : actionResponse.description(); 260 "账户余额不足" : actionResponse.description();
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.ai.config; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.ai.config;
2 2
3 import cn.iocoder.yudao.framework.ai.core.factory.AiClientFactory; 3 import cn.iocoder.yudao.framework.ai.core.factory.AiClientFactory;
4 import cn.iocoder.yudao.framework.ai.core.factory.AiClientFactoryImpl; 4 import cn.iocoder.yudao.framework.ai.core.factory.AiClientFactoryImpl;
  5 +import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
5 import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; 6 import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
6 import cn.iocoder.yudao.framework.ai.core.model.tongyi.QianWenChatClient; 7 import cn.iocoder.yudao.framework.ai.core.model.tongyi.QianWenChatClient;
7 import cn.iocoder.yudao.framework.ai.core.model.tongyi.QianWenChatModal; 8 import cn.iocoder.yudao.framework.ai.core.model.tongyi.QianWenChatModal;
@@ -97,6 +98,13 @@ public class YudaoAiAutoConfiguration { @@ -97,6 +98,13 @@ public class YudaoAiAutoConfiguration {
97 } 98 }
98 99
99 @Bean 100 @Bean
  101 + @ConditionalOnProperty(value = "yudao.ai.midjourney.enable", havingValue = "true")
  102 + public MidjourneyApi midjourneyApi(YudaoAiProperties yudaoAiProperties) {
  103 + YudaoAiProperties.MidjourneyProperties config = yudaoAiProperties.getMidjourney();
  104 + return new MidjourneyApi(config.getBaseUrl(), config.getApiKey(), config.getNotifyUrl());
  105 + }
  106 +
  107 + @Bean
100 @ConditionalOnProperty(value = "yudao.ai.suno.enable", havingValue = "true") 108 @ConditionalOnProperty(value = "yudao.ai.suno.enable", havingValue = "true")
101 public SunoApi sunoApi(YudaoAiProperties yudaoAiProperties) { 109 public SunoApi sunoApi(YudaoAiProperties yudaoAiProperties) {
102 return new SunoApi(yudaoAiProperties.getSuno().getBaseUrl()); 110 return new SunoApi(yudaoAiProperties.getSuno().getBaseUrl());
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiProperties.java
@@ -64,15 +64,18 @@ public class YudaoAiProperties { @@ -64,15 +64,18 @@ public class YudaoAiProperties {
64 @Data 64 @Data
65 @Accessors(chain = true) 65 @Accessors(chain = true)
66 public static class XingHuoProperties extends ChatProperties { 66 public static class XingHuoProperties extends ChatProperties {
  67 +
67 private String appId; 68 private String appId;
68 private String appKey; 69 private String appKey;
69 private String secretKey; 70 private String secretKey;
70 private XingHuoChatModel model; 71 private XingHuoChatModel model;
  72 +
71 } 73 }
72 74
73 @Data 75 @Data
74 @Accessors(chain = true) 76 @Accessors(chain = true)
75 public static class YiYanProperties extends ChatProperties { 77 public static class YiYanProperties extends ChatProperties {
  78 +
76 /** 79 /**
77 * appKey 80 * appKey
78 */ 81 */
@@ -92,26 +95,13 @@ public class YudaoAiProperties { @@ -92,26 +95,13 @@ public class YudaoAiProperties {
92 } 95 }
93 96
94 @Data 97 @Data
95 - @Accessors(chain = true)  
96 public static class MidjourneyProperties { 98 public static class MidjourneyProperties {
97 - private boolean enable = false;  
98 99
99 - /**  
100 - * socket 链接地址  
101 - */  
102 - private String wssUrl = "wss://gateway.discord.gg";  
103 - /**  
104 - * token  
105 - */  
106 - private String token;  
107 - /**  
108 - * 服务id  
109 - */  
110 - private String guildId;  
111 - /**  
112 - * 频道id  
113 - */  
114 - private String channelId; 100 + private String enable;
  101 + private String apiKey;
  102 + private String baseUrl;
  103 + private String notifyUrl;
  104 +
115 } 105 }
116 106
117 @Data 107 @Data
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/midjourney/api/MidjourneyApi.java
1 package cn.iocoder.yudao.framework.ai.core.model.midjourney.api; 1 package cn.iocoder.yudao.framework.ai.core.model.midjourney.api;
2 2
3 -import cn.iocoder.yudao.framework.ai.core.model.midjourney.MidjourneyConfig; 3 +import cn.hutool.core.util.StrUtil;
4 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 4 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
5 import com.google.common.collect.ImmutableMap; 5 import com.google.common.collect.ImmutableMap;
6 import com.google.common.collect.Lists; 6 import com.google.common.collect.Lists;
7 import lombok.AllArgsConstructor; 7 import lombok.AllArgsConstructor;
  8 +import lombok.Data;
8 import lombok.Getter; 9 import lombok.Getter;
9 import lombok.extern.slf4j.Slf4j; 10 import lombok.extern.slf4j.Slf4j;
10 import org.springframework.ai.openai.api.ApiUtils; 11 import org.springframework.ai.openai.api.ApiUtils;
@@ -26,11 +27,17 @@ public class MidjourneyApi { @@ -26,11 +27,17 @@ public class MidjourneyApi {
26 27
27 private final WebClient webClient; 28 private final WebClient webClient;
28 29
29 - public MidjourneyApi(MidjourneyConfig midjourneyConfig) { 30 + /**
  31 + * 回调地址
  32 + */
  33 + private final String notifyUrl;
  34 +
  35 + public MidjourneyApi(String baseUrl, String apiKey, String notifyUrl) {
30 this.webClient = WebClient.builder() 36 this.webClient = WebClient.builder()
31 - .baseUrl(midjourneyConfig.getUrl())  
32 - .defaultHeaders(ApiUtils.getJsonContentHeaders(midjourneyConfig.getKey())) 37 + .baseUrl(baseUrl)
  38 + .defaultHeaders(ApiUtils.getJsonContentHeaders(apiKey))
33 .build(); 39 .build();
  40 + this.notifyUrl = notifyUrl;
34 } 41 }
35 42
36 /** 43 /**
@@ -40,6 +47,9 @@ public class MidjourneyApi { @@ -40,6 +47,9 @@ public class MidjourneyApi {
40 * @return 提交结果 47 * @return 提交结果
41 */ 48 */
42 public SubmitResponse imagine(ImagineRequest request) { 49 public SubmitResponse imagine(ImagineRequest request) {
  50 + if (StrUtil.isEmpty(request.getNotifyHook())) {
  51 + request.setNotifyHook(notifyUrl);
  52 + }
43 String response = post("/submit/imagine", request); 53 String response = post("/submit/imagine", request);
44 return JsonUtils.parseObject(response, SubmitResponse.class); 54 return JsonUtils.parseObject(response, SubmitResponse.class);
45 } 55 }
@@ -51,8 +61,11 @@ public class MidjourneyApi { @@ -51,8 +61,11 @@ public class MidjourneyApi {
51 * @return 提交结果 61 * @return 提交结果
52 */ 62 */
53 public SubmitResponse action(ActionRequest request) { 63 public SubmitResponse action(ActionRequest request) {
54 - String res = post("/submit/action", request);  
55 - return JsonUtils.parseObject(res, SubmitResponse.class); 64 + if (StrUtil.isEmpty(request.getNotifyHook())) {
  65 + request.setNotifyHook(notifyUrl);
  66 + }
  67 + String response = post("/submit/action", request);
  68 + return JsonUtils.parseObject(response, SubmitResponse.class);
56 } 69 }
57 70
58 /** 71 /**
@@ -86,23 +99,40 @@ public class MidjourneyApi { @@ -86,23 +99,40 @@ public class MidjourneyApi {
86 99
87 /** 100 /**
88 * Imagine 请求(生成图片) 101 * Imagine 请求(生成图片)
89 - *  
90 - * @param base64Array 垫图(参考图) base64数 组  
91 - * @param notifyHook 通知地址  
92 - * @param prompt 提示词  
93 - * @param state 自定义参数  
94 */ 102 */
95 - public record ImagineRequest(List<String> base64Array,  
96 - String notifyHook,  
97 - String prompt,  
98 - String state) { 103 + @Data
  104 + public static final class ImagineRequest {
  105 +
  106 + /**
  107 + * 垫图(参考图) base64 数组
  108 + */
  109 + private List<String> base64Array;
  110 + /**
  111 + * 提示词
  112 + */
  113 + private String prompt;
  114 + /**
  115 + * 通知地址
  116 + */
  117 + private String notifyHook;
  118 + /**
  119 + * 自定义参数
  120 + */
  121 + private String state;
  122 +
  123 + public ImagineRequest(List<String> base64Array, String prompt, String notifyHook, String state) {
  124 + this.base64Array = base64Array;
  125 + this.prompt = prompt;
  126 + this.notifyHook = notifyHook;
  127 + this.state = state;
  128 + }
99 129
100 public static String buildState(Integer width, Integer height, String version, String model) { 130 public static String buildState(Integer width, Integer height, String version, String model) {
101 StringBuilder params = new StringBuilder(); 131 StringBuilder params = new StringBuilder();
102 // --ar 来设置尺寸 132 // --ar 来设置尺寸
103 params.append(String.format(" --ar %s:%s ", width, height)); 133 params.append(String.format(" --ar %s:%s ", width, height));
104 // --niji 模型 134 // --niji 模型
105 - if (MidjourneyApi.ModelEnum.NIJI.getModel().equals(model)) { 135 + if (ModelEnum.NIJI.getModel().equals(model)) {
106 params.append(String.format(" --niji %s ", version)); 136 params.append(String.format(" --niji %s ", version));
107 } else { 137 } else {
108 params.append(String.format(" --v %s ", version)); 138 params.append(String.format(" --v %s ", version));
@@ -114,15 +144,20 @@ public class MidjourneyApi { @@ -114,15 +144,20 @@ public class MidjourneyApi {
114 144
115 /** 145 /**
116 * Action 请求 146 * Action 请求
117 - *  
118 - * @param customId 操作按钮id  
119 - * @param taskId 操作按钮id  
120 - * @param notifyHook 通知地址  
121 */ 147 */
122 - public record ActionRequest(String customId,  
123 - String taskId,  
124 - String notifyHook  
125 - ) { 148 + @Data
  149 + public static final class ActionRequest {
  150 +
  151 + private String customId;
  152 + private String taskId;
  153 + private String notifyHook;
  154 +
  155 + public ActionRequest(String taskId, String customId, String notifyHook) {
  156 + this.customId = customId;
  157 + this.taskId = taskId;
  158 + this.notifyHook = notifyHook;
  159 + }
  160 +
126 } 161 }
127 162
128 /** 163 /**
yudao-server/src/main/resources/application.yaml
@@ -194,20 +194,14 @@ yudao.ai: @@ -194,20 +194,14 @@ yudao.ai:
194 api-key: sk-Zsd81gZYg7 194 api-key: sk-Zsd81gZYg7
195 midjourney: 195 midjourney:
196 enable: true 196 enable: true
197 - token: MTE4MjE3MjY2MjkxNTY3ODIzOA.GEV1SG.c49F8lZoGCUHwsj8O0UdodmM6nyQHvuD2fXflw  
198 - guild-id: 1237948819677904956  
199 - channel-id: 1237948819677904960 197 +# base-url: https://api.holdai.top/mj-relax/mj
  198 + base-url: https://api.holdai.top/mj
  199 + api-key: sk-dZEPiVaNcT3FHhef51996bAa0bC74806BeAb620dA5Da10Bf
  200 + notify-url: http://java.nat300.top/admin-api/ai/image/midjourney/notify
200 suno: 201 suno:
201 enable: true 202 enable: true
202 base-url: https://suno-imrqwwui8-status2xxs-projects.vercel.app 203 base-url: https://suno-imrqwwui8-status2xxs-projects.vercel.app
203 204
204 -ai:  
205 - midjourney-proxy:  
206 - enable: true  
207 - url: https://api.holdai.top/mj  
208 - notifyUrl: http://61d61685.r21.cpolar.top/admin-api/ai/image/midjourney-notify  
209 - key: sk-dZEPiVaNcT3FHhef51996bAa0bC74806BeAb620dA5Da10Bf  
210 -  
211 --- #################### 芋道相关配置 #################### 205 --- #################### 芋道相关配置 ####################
212 206
213 yudao: 207 yudao: